Upgrade to ExtJS 3.1.1 - Released 02/08/2010
[extjs.git] / ext-all-debug.js
1 /*!
2  * Ext JS Library 3.1.1
3  * Copyright(c) 2006-2010 Ext JS, LLC
4  * licensing@extjs.com
5  * http://www.extjs.com/license
6  */
7 /**\r
8  * @class Ext.DomHelper\r
9  * <p>The DomHelper class provides a layer of abstraction from DOM and transparently supports creating\r
10  * elements via DOM or using HTML fragments. It also has the ability to create HTML fragment templates\r
11  * from your DOM building code.</p>\r
12  *\r
13  * <p><b><u>DomHelper element specification object</u></b></p>\r
14  * <p>A specification object is used when creating elements. Attributes of this object\r
15  * are assumed to be element attributes, except for 4 special attributes:\r
16  * <div class="mdetail-params"><ul>\r
17  * <li><b><tt>tag</tt></b> : <div class="sub-desc">The tag name of the element</div></li>\r
18  * <li><b><tt>children</tt></b> : or <tt>cn</tt><div class="sub-desc">An array of the\r
19  * same kind of element definition objects to be created and appended. These can be nested\r
20  * as deep as you want.</div></li>\r
21  * <li><b><tt>cls</tt></b> : <div class="sub-desc">The class attribute of the element.\r
22  * This will end up being either the "class" attribute on a HTML fragment or className\r
23  * for a DOM node, depending on whether DomHelper is using fragments or DOM.</div></li>\r
24  * <li><b><tt>html</tt></b> : <div class="sub-desc">The innerHTML for the element</div></li>\r
25  * </ul></div></p>\r
26  *\r
27  * <p><b><u>Insertion methods</u></b></p>\r
28  * <p>Commonly used insertion methods:\r
29  * <div class="mdetail-params"><ul>\r
30  * <li><b><tt>{@link #append}</tt></b> : <div class="sub-desc"></div></li>\r
31  * <li><b><tt>{@link #insertBefore}</tt></b> : <div class="sub-desc"></div></li>\r
32  * <li><b><tt>{@link #insertAfter}</tt></b> : <div class="sub-desc"></div></li>\r
33  * <li><b><tt>{@link #overwrite}</tt></b> : <div class="sub-desc"></div></li>\r
34  * <li><b><tt>{@link #createTemplate}</tt></b> : <div class="sub-desc"></div></li>\r
35  * <li><b><tt>{@link #insertHtml}</tt></b> : <div class="sub-desc"></div></li>\r
36  * </ul></div></p>\r
37  *\r
38  * <p><b><u>Example</u></b></p>\r
39  * <p>This is an example, where an unordered list with 3 children items is appended to an existing\r
40  * element with id <tt>'my-div'</tt>:<br>\r
41  <pre><code>\r
42 var dh = Ext.DomHelper; // create shorthand alias\r
43 // specification object\r
44 var spec = {\r
45     id: 'my-ul',\r
46     tag: 'ul',\r
47     cls: 'my-list',\r
48     // append children after creating\r
49     children: [     // may also specify 'cn' instead of 'children'\r
50         {tag: 'li', id: 'item0', html: 'List Item 0'},\r
51         {tag: 'li', id: 'item1', html: 'List Item 1'},\r
52         {tag: 'li', id: 'item2', html: 'List Item 2'}\r
53     ]\r
54 };\r
55 var list = dh.append(\r
56     'my-div', // the context element 'my-div' can either be the id or the actual node\r
57     spec      // the specification object\r
58 );\r
59  </code></pre></p>\r
60  * <p>Element creation specification parameters in this class may also be passed as an Array of\r
61  * specification objects. This can be used to insert multiple sibling nodes into an existing\r
62  * container very efficiently. For example, to add more list items to the example above:<pre><code>\r
63 dh.append('my-ul', [\r
64     {tag: 'li', id: 'item3', html: 'List Item 3'},\r
65     {tag: 'li', id: 'item4', html: 'List Item 4'}\r
66 ]);\r
67  * </code></pre></p>\r
68  *\r
69  * <p><b><u>Templating</u></b></p>\r
70  * <p>The real power is in the built-in templating. Instead of creating or appending any elements,\r
71  * <tt>{@link #createTemplate}</tt> returns a Template object which can be used over and over to\r
72  * insert new elements. Revisiting the example above, we could utilize templating this time:\r
73  * <pre><code>\r
74 // create the node\r
75 var list = dh.append('my-div', {tag: 'ul', cls: 'my-list'});\r
76 // get template\r
77 var tpl = dh.createTemplate({tag: 'li', id: 'item{0}', html: 'List Item {0}'});\r
78 \r
79 for(var i = 0; i < 5, i++){\r
80     tpl.append(list, [i]); // use template to append to the actual node\r
81 }\r
82  * </code></pre></p>\r
83  * <p>An example using a template:<pre><code>\r
84 var html = '<a id="{0}" href="{1}" class="nav">{2}</a>';\r
85 \r
86 var tpl = new Ext.DomHelper.createTemplate(html);\r
87 tpl.append('blog-roll', ['link1', 'http://www.jackslocum.com/', "Jack&#39;s Site"]);\r
88 tpl.append('blog-roll', ['link2', 'http://www.dustindiaz.com/', "Dustin&#39;s Site"]);\r
89  * </code></pre></p>\r
90  *\r
91  * <p>The same example using named parameters:<pre><code>\r
92 var html = '<a id="{id}" href="{url}" class="nav">{text}</a>';\r
93 \r
94 var tpl = new Ext.DomHelper.createTemplate(html);\r
95 tpl.append('blog-roll', {\r
96     id: 'link1',\r
97     url: 'http://www.jackslocum.com/',\r
98     text: "Jack&#39;s Site"\r
99 });\r
100 tpl.append('blog-roll', {\r
101     id: 'link2',\r
102     url: 'http://www.dustindiaz.com/',\r
103     text: "Dustin&#39;s Site"\r
104 });\r
105  * </code></pre></p>\r
106  *\r
107  * <p><b><u>Compiling Templates</u></b></p>\r
108  * <p>Templates are applied using regular expressions. The performance is great, but if\r
109  * you are adding a bunch of DOM elements using the same template, you can increase\r
110  * performance even further by {@link Ext.Template#compile "compiling"} the template.\r
111  * The way "{@link Ext.Template#compile compile()}" works is the template is parsed and\r
112  * broken up at the different variable points and a dynamic function is created and eval'ed.\r
113  * The generated function performs string concatenation of these parts and the passed\r
114  * variables instead of using regular expressions.\r
115  * <pre><code>\r
116 var html = '<a id="{id}" href="{url}" class="nav">{text}</a>';\r
117 \r
118 var tpl = new Ext.DomHelper.createTemplate(html);\r
119 tpl.compile();\r
120 \r
121 //... use template like normal\r
122  * </code></pre></p>\r
123  *\r
124  * <p><b><u>Performance Boost</u></b></p>\r
125  * <p>DomHelper will transparently create HTML fragments when it can. Using HTML fragments instead\r
126  * of DOM can significantly boost performance.</p>\r
127  * <p>Element creation specification parameters may also be strings. If {@link #useDom} is <tt>false</tt>,\r
128  * then the string is used as innerHTML. If {@link #useDom} is <tt>true</tt>, a string specification\r
129  * results in the creation of a text node. Usage:</p>\r
130  * <pre><code>\r
131 Ext.DomHelper.useDom = true; // force it to use DOM; reduces performance\r
132  * </code></pre>\r
133  * @singleton\r
134  */\r
135 Ext.DomHelper = function(){\r
136     var tempTableEl = null,\r
137         emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i,\r
138         tableRe = /^table|tbody|tr|td$/i,\r
139         pub,\r
140         // kill repeat to save bytes\r
141         afterbegin = 'afterbegin',\r
142         afterend = 'afterend',\r
143         beforebegin = 'beforebegin',\r
144         beforeend = 'beforeend',\r
145         ts = '<table>',\r
146         te = '</table>',\r
147         tbs = ts+'<tbody>',\r
148         tbe = '</tbody>'+te,\r
149         trs = tbs + '<tr>',\r
150         tre = '</tr>'+tbe;\r
151 \r
152     // private\r
153     function doInsert(el, o, returnElement, pos, sibling, append){\r
154         var newNode = pub.insertHtml(pos, Ext.getDom(el), createHtml(o));\r
155         return returnElement ? Ext.get(newNode, true) : newNode;\r
156     }\r
157 \r
158     // build as innerHTML where available\r
159     function createHtml(o){\r
160         var b = '',\r
161             attr,\r
162             val,\r
163             key,\r
164             keyVal,\r
165             cn;\r
166 \r
167         if(Ext.isString(o)){\r
168             b = o;\r
169         } else if (Ext.isArray(o)) {\r
170             for (var i=0; i < o.length; i++) {\r
171                 if(o[i]) {\r
172                     b += createHtml(o[i]);\r
173                 }\r
174             };\r
175         } else {\r
176             b += '<' + (o.tag = o.tag || 'div');\r
177             Ext.iterate(o, function(attr, val){\r
178                 if(!/tag|children|cn|html$/i.test(attr)){\r
179                     if (Ext.isObject(val)) {\r
180                         b += ' ' + attr + '="';\r
181                         Ext.iterate(val, function(key, keyVal){\r
182                             b += key + ':' + keyVal + ';';\r
183                         });\r
184                         b += '"';\r
185                     }else{\r
186                         b += ' ' + ({cls : 'class', htmlFor : 'for'}[attr] || attr) + '="' + val + '"';\r
187                     }\r
188                 }\r
189             });\r
190             // Now either just close the tag or try to add children and close the tag.\r
191             if (emptyTags.test(o.tag)) {\r
192                 b += '/>';\r
193             } else {\r
194                 b += '>';\r
195                 if ((cn = o.children || o.cn)) {\r
196                     b += createHtml(cn);\r
197                 } else if(o.html){\r
198                     b += o.html;\r
199                 }\r
200                 b += '</' + o.tag + '>';\r
201             }\r
202         }\r
203         return b;\r
204     }\r
205 \r
206     function ieTable(depth, s, h, e){\r
207         tempTableEl.innerHTML = [s, h, e].join('');\r
208         var i = -1,\r
209             el = tempTableEl,\r
210             ns;\r
211         while(++i < depth){\r
212             el = el.firstChild;\r
213         }\r
214 //      If the result is multiple siblings, then encapsulate them into one fragment.\r
215         if(ns = el.nextSibling){\r
216             var df = document.createDocumentFragment();\r
217             while(el){\r
218                 ns = el.nextSibling;\r
219                 df.appendChild(el);\r
220                 el = ns;\r
221             }\r
222             el = df;\r
223         }\r
224         return el;\r
225     }\r
226 \r
227     /**\r
228      * @ignore\r
229      * Nasty code for IE's broken table implementation\r
230      */\r
231     function insertIntoTable(tag, where, el, html) {\r
232         var node,\r
233             before;\r
234 \r
235         tempTableEl = tempTableEl || document.createElement('div');\r
236 \r
237         if(tag == 'td' && (where == afterbegin || where == beforeend) ||\r
238            !/td|tr|tbody/i.test(tag) && (where == beforebegin || where == afterend)) {\r
239             return;\r
240         }\r
241         before = where == beforebegin ? el :\r
242                  where == afterend ? el.nextSibling :\r
243                  where == afterbegin ? el.firstChild : null;\r
244 \r
245         if (where == beforebegin || where == afterend) {\r
246             el = el.parentNode;\r
247         }\r
248 \r
249         if (tag == 'td' || (tag == 'tr' && (where == beforeend || where == afterbegin))) {\r
250             node = ieTable(4, trs, html, tre);\r
251         } else if ((tag == 'tbody' && (where == beforeend || where == afterbegin)) ||\r
252                    (tag == 'tr' && (where == beforebegin || where == afterend))) {\r
253             node = ieTable(3, tbs, html, tbe);\r
254         } else {\r
255             node = ieTable(2, ts, html, te);\r
256         }\r
257         el.insertBefore(node, before);\r
258         return node;\r
259     }\r
260 \r
261 \r
262     pub = {\r
263         /**\r
264          * Returns the markup for the passed Element(s) config.\r
265          * @param {Object} o The DOM object spec (and children)\r
266          * @return {String}\r
267          */\r
268         markup : function(o){\r
269             return createHtml(o);\r
270         },\r
271         \r
272         /**\r
273          * Applies a style specification to an element.\r
274          * @param {String/HTMLElement} el The element to apply styles to\r
275          * @param {String/Object/Function} styles A style specification string e.g. 'width:100px', or object in the form {width:'100px'}, or\r
276          * a function which returns such a specification.\r
277          */\r
278         applyStyles : function(el, styles){\r
279             if(styles){\r
280                 var i = 0,\r
281                     len,\r
282                     style;\r
283 \r
284                 el = Ext.fly(el);\r
285                 if(Ext.isFunction(styles)){\r
286                     styles = styles.call();\r
287                 }\r
288                 if(Ext.isString(styles)){\r
289                     styles = styles.trim().split(/\s*(?::|;)\s*/);\r
290                     for(len = styles.length; i < len;){\r
291                         el.setStyle(styles[i++], styles[i++]);\r
292                     }\r
293                 }else if (Ext.isObject(styles)){\r
294                     el.setStyle(styles);\r
295                 }\r
296             }\r
297         },\r
298 \r
299         /**\r
300          * Inserts an HTML fragment into the DOM.\r
301          * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.\r
302          * @param {HTMLElement} el The context element\r
303          * @param {String} html The HTML fragment\r
304          * @return {HTMLElement} The new node\r
305          */\r
306         insertHtml : function(where, el, html){\r
307             var hash = {},\r
308                 hashVal,\r
309                 setStart,\r
310                 range,\r
311                 frag,\r
312                 rangeEl,\r
313                 rs;\r
314 \r
315             where = where.toLowerCase();\r
316             // add these here because they are used in both branches of the condition.\r
317             hash[beforebegin] = ['BeforeBegin', 'previousSibling'];\r
318             hash[afterend] = ['AfterEnd', 'nextSibling'];\r
319 \r
320             if (el.insertAdjacentHTML) {\r
321                 if(tableRe.test(el.tagName) && (rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html))){\r
322                     return rs;\r
323                 }\r
324                 // add these two to the hash.\r
325                 hash[afterbegin] = ['AfterBegin', 'firstChild'];\r
326                 hash[beforeend] = ['BeforeEnd', 'lastChild'];\r
327                 if ((hashVal = hash[where])) {\r
328                     el.insertAdjacentHTML(hashVal[0], html);\r
329                     return el[hashVal[1]];\r
330                 }\r
331             } else {\r
332                 range = el.ownerDocument.createRange();\r
333                 setStart = 'setStart' + (/end/i.test(where) ? 'After' : 'Before');\r
334                 if (hash[where]) {\r
335                     range[setStart](el);\r
336                     frag = range.createContextualFragment(html);\r
337                     el.parentNode.insertBefore(frag, where == beforebegin ? el : el.nextSibling);\r
338                     return el[(where == beforebegin ? 'previous' : 'next') + 'Sibling'];\r
339                 } else {\r
340                     rangeEl = (where == afterbegin ? 'first' : 'last') + 'Child';\r
341                     if (el.firstChild) {\r
342                         range[setStart](el[rangeEl]);\r
343                         frag = range.createContextualFragment(html);\r
344                         if(where == afterbegin){\r
345                             el.insertBefore(frag, el.firstChild);\r
346                         }else{\r
347                             el.appendChild(frag);\r
348                         }\r
349                     } else {\r
350                         el.innerHTML = html;\r
351                     }\r
352                     return el[rangeEl];\r
353                 }\r
354             }\r
355             throw 'Illegal insertion point -> "' + where + '"';\r
356         },\r
357 \r
358         /**\r
359          * Creates new DOM element(s) and inserts them before el.\r
360          * @param {Mixed} el The context element\r
361          * @param {Object/String} o The DOM object spec (and children) or raw HTML blob\r
362          * @param {Boolean} returnElement (optional) true to return a Ext.Element\r
363          * @return {HTMLElement/Ext.Element} The new node\r
364          */\r
365         insertBefore : function(el, o, returnElement){\r
366             return doInsert(el, o, returnElement, beforebegin);\r
367         },\r
368 \r
369         /**\r
370          * Creates new DOM element(s) and inserts them after el.\r
371          * @param {Mixed} el The context element\r
372          * @param {Object} o The DOM object spec (and children)\r
373          * @param {Boolean} returnElement (optional) true to return a Ext.Element\r
374          * @return {HTMLElement/Ext.Element} The new node\r
375          */\r
376         insertAfter : function(el, o, returnElement){\r
377             return doInsert(el, o, returnElement, afterend, 'nextSibling');\r
378         },\r
379 \r
380         /**\r
381          * Creates new DOM element(s) and inserts them as the first child of el.\r
382          * @param {Mixed} el The context element\r
383          * @param {Object/String} o The DOM object spec (and children) or raw HTML blob\r
384          * @param {Boolean} returnElement (optional) true to return a Ext.Element\r
385          * @return {HTMLElement/Ext.Element} The new node\r
386          */\r
387         insertFirst : function(el, o, returnElement){\r
388             return doInsert(el, o, returnElement, afterbegin, 'firstChild');\r
389         },\r
390 \r
391         /**\r
392          * Creates new DOM element(s) and appends them to el.\r
393          * @param {Mixed} el The context element\r
394          * @param {Object/String} o The DOM object spec (and children) or raw HTML blob\r
395          * @param {Boolean} returnElement (optional) true to return a Ext.Element\r
396          * @return {HTMLElement/Ext.Element} The new node\r
397          */\r
398         append : function(el, o, returnElement){\r
399             return doInsert(el, o, returnElement, beforeend, '', true);\r
400         },\r
401 \r
402         /**\r
403          * Creates new DOM element(s) and overwrites the contents of el with them.\r
404          * @param {Mixed} el The context element\r
405          * @param {Object/String} o The DOM object spec (and children) or raw HTML blob\r
406          * @param {Boolean} returnElement (optional) true to return a Ext.Element\r
407          * @return {HTMLElement/Ext.Element} The new node\r
408          */\r
409         overwrite : function(el, o, returnElement){\r
410             el = Ext.getDom(el);\r
411             el.innerHTML = createHtml(o);\r
412             return returnElement ? Ext.get(el.firstChild) : el.firstChild;\r
413         },\r
414 \r
415         createHtml : createHtml\r
416     };\r
417     return pub;\r
418 }();/**\r
419  * @class Ext.DomHelper\r
420  */\r
421 Ext.apply(Ext.DomHelper,\r
422 function(){\r
423         var pub,\r
424                 afterbegin = 'afterbegin',\r
425         afterend = 'afterend',\r
426         beforebegin = 'beforebegin',\r
427         beforeend = 'beforeend';\r
428 \r
429         // private\r
430     function doInsert(el, o, returnElement, pos, sibling, append){\r
431         el = Ext.getDom(el);\r
432         var newNode;\r
433         if (pub.useDom) {\r
434             newNode = createDom(o, null);\r
435             if (append) {\r
436                     el.appendChild(newNode);\r
437             } else {\r
438                         (sibling == 'firstChild' ? el : el.parentNode).insertBefore(newNode, el[sibling] || el);\r
439             }\r
440         } else {\r
441             newNode = Ext.DomHelper.insertHtml(pos, el, Ext.DomHelper.createHtml(o));\r
442         }\r
443         return returnElement ? Ext.get(newNode, true) : newNode;\r
444     }\r
445 \r
446         // build as dom\r
447     /** @ignore */\r
448     function createDom(o, parentNode){\r
449         var el,\r
450                 doc = document,\r
451                 useSet,\r
452                 attr,\r
453                 val,\r
454                 cn;\r
455 \r
456         if (Ext.isArray(o)) {                       // Allow Arrays of siblings to be inserted\r
457             el = doc.createDocumentFragment(); // in one shot using a DocumentFragment\r
458                 Ext.each(o, function(v) {\r
459                 createDom(v, el);\r
460             });\r
461         } else if (Ext.isString(o)) {         // Allow a string as a child spec.\r
462             el = doc.createTextNode(o);\r
463         } else {\r
464             el = doc.createElement( o.tag || 'div' );\r
465             useSet = !!el.setAttribute; // In IE some elements don't have setAttribute\r
466             Ext.iterate(o, function(attr, val){\r
467                 if(!/tag|children|cn|html|style/.test(attr)){\r
468                         if(attr == 'cls'){\r
469                             el.className = val;\r
470                         }else{\r
471                         if(useSet){\r
472                             el.setAttribute(attr, val);\r
473                         }else{\r
474                             el[attr] = val;\r
475                         }\r
476                         }\r
477                 }\r
478             });\r
479             Ext.DomHelper.applyStyles(el, o.style);\r
480 \r
481             if ((cn = o.children || o.cn)) {\r
482                 createDom(cn, el);\r
483             } else if (o.html) {\r
484                 el.innerHTML = o.html;\r
485             }\r
486         }\r
487         if(parentNode){\r
488            parentNode.appendChild(el);\r
489         }\r
490         return el;\r
491     }\r
492 \r
493         pub = {\r
494                 /**\r
495              * Creates a new Ext.Template from the DOM object spec.\r
496              * @param {Object} o The DOM object spec (and children)\r
497              * @return {Ext.Template} The new template\r
498              */\r
499             createTemplate : function(o){\r
500                 var html = Ext.DomHelper.createHtml(o);\r
501                 return new Ext.Template(html);\r
502             },\r
503 \r
504                 /** True to force the use of DOM instead of html fragments @type Boolean */\r
505             useDom : false,\r
506 \r
507             /**\r
508              * Creates new DOM element(s) and inserts them before el.\r
509              * @param {Mixed} el The context element\r
510              * @param {Object/String} o The DOM object spec (and children) or raw HTML blob\r
511              * @param {Boolean} returnElement (optional) true to return a Ext.Element\r
512              * @return {HTMLElement/Ext.Element} The new node\r
513          * @hide (repeat)\r
514              */\r
515             insertBefore : function(el, o, returnElement){\r
516                 return doInsert(el, o, returnElement, beforebegin);\r
517             },\r
518 \r
519             /**\r
520              * Creates new DOM element(s) and inserts them after el.\r
521              * @param {Mixed} el The context element\r
522              * @param {Object} o The DOM object spec (and children)\r
523              * @param {Boolean} returnElement (optional) true to return a Ext.Element\r
524              * @return {HTMLElement/Ext.Element} The new node\r
525          * @hide (repeat)\r
526              */\r
527             insertAfter : function(el, o, returnElement){\r
528                 return doInsert(el, o, returnElement, afterend, 'nextSibling');\r
529             },\r
530 \r
531             /**\r
532              * Creates new DOM element(s) and inserts them as the first child of el.\r
533              * @param {Mixed} el The context element\r
534              * @param {Object/String} o The DOM object spec (and children) or raw HTML blob\r
535              * @param {Boolean} returnElement (optional) true to return a Ext.Element\r
536              * @return {HTMLElement/Ext.Element} The new node\r
537          * @hide (repeat)\r
538              */\r
539             insertFirst : function(el, o, returnElement){\r
540                 return doInsert(el, o, returnElement, afterbegin, 'firstChild');\r
541             },\r
542 \r
543             /**\r
544              * Creates new DOM element(s) and appends them to el.\r
545              * @param {Mixed} el The context element\r
546              * @param {Object/String} o The DOM object spec (and children) or raw HTML blob\r
547              * @param {Boolean} returnElement (optional) true to return a Ext.Element\r
548              * @return {HTMLElement/Ext.Element} The new node\r
549          * @hide (repeat)\r
550              */\r
551             append: function(el, o, returnElement){\r
552             return doInsert(el, o, returnElement, beforeend, '', true);\r
553         },\r
554 \r
555             /**\r
556              * Creates new DOM element(s) without inserting them to the document.\r
557              * @param {Object/String} o The DOM object spec (and children) or raw HTML blob\r
558              * @return {HTMLElement} The new uninserted node\r
559              */\r
560         createDom: createDom\r
561         };\r
562         return pub;\r
563 }());/**
564  * @class Ext.Template
565  * <p>Represents an HTML fragment template. Templates may be {@link #compile precompiled}
566  * for greater performance.</p>
567  * <p>For example usage {@link #Template see the constructor}.</p>
568  * 
569  * @constructor
570  * An instance of this class may be created by passing to the constructor either
571  * a single argument, or multiple arguments:
572  * <div class="mdetail-params"><ul>
573  * <li><b>single argument</b> : String/Array
574  * <div class="sub-desc">
575  * The single argument may be either a String or an Array:<ul>
576  * <li><tt>String</tt> : </li><pre><code>
577 var t = new Ext.Template("&lt;div>Hello {0}.&lt;/div>");
578 t.{@link #append}('some-element', ['foo']);
579  * </code></pre>
580  * <li><tt>Array</tt> : </li>
581  * An Array will be combined with <code>join('')</code>.
582 <pre><code>
583 var t = new Ext.Template([
584     '&lt;div name="{id}"&gt;',
585         '&lt;span class="{cls}"&gt;{name:trim} {value:ellipsis(10)}&lt;/span&gt;',
586     '&lt;/div&gt;',
587 ]);
588 t.{@link #compile}();
589 t.{@link #append}('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
590 </code></pre>
591  * </ul></div></li>
592  * <li><b>multiple arguments</b> : String, Object, Array, ...
593  * <div class="sub-desc">
594  * Multiple arguments will be combined with <code>join('')</code>.
595  * <pre><code>
596 var t = new Ext.Template(
597     '&lt;div name="{id}"&gt;',
598         '&lt;span class="{cls}"&gt;{name} {value}&lt;/span&gt;',
599     '&lt;/div&gt;',
600     // a configuration object:
601     {
602         compiled: true,      // {@link #compile} immediately
603         disableFormats: true // See Notes below.
604     } 
605 );
606  * </code></pre>
607  * <p><b>Notes</b>:</p>
608  * <div class="mdetail-params"><ul>
609  * <li>Formatting and <code>disableFormats</code> are not applicable for Ext Core.</li>
610  * <li>For a list of available format functions, see {@link Ext.util.Format}.</li>
611  * <li><code>disableFormats</code> reduces <code>{@link #apply}</code> time
612  * when no formatting is required.</li>
613  * </ul></div>
614  * </div></li>
615  * </ul></div>
616  * @param {Mixed} config
617  */
618 Ext.Template = function(html){
619     var me = this,
620         a = arguments,
621         buf = [];
622
623     if (Ext.isArray(html)) {
624         html = html.join("");
625     } else if (a.length > 1) {
626             Ext.each(a, function(v) {
627             if (Ext.isObject(v)) {
628                 Ext.apply(me, v);
629             } else {
630                 buf.push(v);
631             }
632         });
633         html = buf.join('');
634     }
635
636     /**@private*/
637     me.html = html;
638     /**
639      * @cfg {Boolean} compiled Specify <tt>true</tt> to compile the template
640      * immediately (see <code>{@link #compile}</code>).
641      * Defaults to <tt>false</tt>.
642      */
643     if (me.compiled) {
644         me.compile();
645     }
646 };
647 Ext.Template.prototype = {
648     /**
649      * @cfg {RegExp} re The regular expression used to match template variables.
650      * Defaults to:<pre><code>
651      * re : /\{([\w-]+)\}/g                                     // for Ext Core
652      * re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g      // for Ext JS
653      * </code></pre>
654      */
655     re : /\{([\w-]+)\}/g,
656     /**
657      * See <code>{@link #re}</code>.
658      * @type RegExp
659      * @property re
660      */
661
662     /**
663      * Returns an HTML fragment of this template with the specified <code>values</code> applied.
664      * @param {Object/Array} values
665      * The template values. Can be an array if the params are numeric (i.e. <code>{0}</code>)
666      * or an object (i.e. <code>{foo: 'bar'}</code>).
667      * @return {String} The HTML fragment
668      */
669     applyTemplate : function(values){
670                 var me = this;
671
672         return me.compiled ?
673                         me.compiled(values) :
674                                 me.html.replace(me.re, function(m, name){
675                                 return values[name] !== undefined ? values[name] : "";
676                         });
677         },
678
679     /**
680      * Sets the HTML used as the template and optionally compiles it.
681      * @param {String} html
682      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
683      * @return {Ext.Template} this
684      */
685     set : function(html, compile){
686             var me = this;
687         me.html = html;
688         me.compiled = null;
689         return compile ? me.compile() : me;
690     },
691
692     /**
693      * Compiles the template into an internal function, eliminating the RegEx overhead.
694      * @return {Ext.Template} this
695      */
696     compile : function(){
697         var me = this,
698                 sep = Ext.isGecko ? "+" : ",";
699
700         function fn(m, name){                        
701                 name = "values['" + name + "']";
702                 return "'"+ sep + '(' + name + " == undefined ? '' : " + name + ')' + sep + "'";
703         }
704                 
705         eval("this.compiled = function(values){ return " + (Ext.isGecko ? "'" : "['") +
706              me.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
707              (Ext.isGecko ?  "';};" : "'].join('');};"));
708         return me;
709     },
710
711     /**
712      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
713      * @param {Mixed} el The context element
714      * @param {Object/Array} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
715      * @param {Boolean} returnElement (optional) true to return a Ext.Element (defaults to undefined)
716      * @return {HTMLElement/Ext.Element} The new node or Element
717      */
718     insertFirst: function(el, values, returnElement){
719         return this.doInsert('afterBegin', el, values, returnElement);
720     },
721
722     /**
723      * Applies the supplied values to the template and inserts the new node(s) before el.
724      * @param {Mixed} el The context element
725      * @param {Object/Array} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
726      * @param {Boolean} returnElement (optional) true to return a Ext.Element (defaults to undefined)
727      * @return {HTMLElement/Ext.Element} The new node or Element
728      */
729     insertBefore: function(el, values, returnElement){
730         return this.doInsert('beforeBegin', el, values, returnElement);
731     },
732
733     /**
734      * Applies the supplied values to the template and inserts the new node(s) after el.
735      * @param {Mixed} el The context element
736      * @param {Object/Array} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
737      * @param {Boolean} returnElement (optional) true to return a Ext.Element (defaults to undefined)
738      * @return {HTMLElement/Ext.Element} The new node or Element
739      */
740     insertAfter : function(el, values, returnElement){
741         return this.doInsert('afterEnd', el, values, returnElement);
742     },
743
744     /**
745      * Applies the supplied <code>values</code> to the template and appends
746      * the new node(s) to the specified <code>el</code>.
747      * <p>For example usage {@link #Template see the constructor}.</p>
748      * @param {Mixed} el The context element
749      * @param {Object/Array} values
750      * The template values. Can be an array if the params are numeric (i.e. <code>{0}</code>)
751      * or an object (i.e. <code>{foo: 'bar'}</code>).
752      * @param {Boolean} returnElement (optional) true to return an Ext.Element (defaults to undefined)
753      * @return {HTMLElement/Ext.Element} The new node or Element
754      */
755     append : function(el, values, returnElement){
756         return this.doInsert('beforeEnd', el, values, returnElement);
757     },
758
759     doInsert : function(where, el, values, returnEl){
760         el = Ext.getDom(el);
761         var newNode = Ext.DomHelper.insertHtml(where, el, this.applyTemplate(values));
762         return returnEl ? Ext.get(newNode, true) : newNode;
763     },
764
765     /**
766      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
767      * @param {Mixed} el The context element
768      * @param {Object/Array} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
769      * @param {Boolean} returnElement (optional) true to return a Ext.Element (defaults to undefined)
770      * @return {HTMLElement/Ext.Element} The new node or Element
771      */
772     overwrite : function(el, values, returnElement){
773         el = Ext.getDom(el);
774         el.innerHTML = this.applyTemplate(values);
775         return returnElement ? Ext.get(el.firstChild, true) : el.firstChild;
776     }
777 };
778 /**
779  * Alias for {@link #applyTemplate}
780  * Returns an HTML fragment of this template with the specified <code>values</code> applied.
781  * @param {Object/Array} values
782  * The template values. Can be an array if the params are numeric (i.e. <code>{0}</code>)
783  * or an object (i.e. <code>{foo: 'bar'}</code>).
784  * @return {String} The HTML fragment
785  * @member Ext.Template
786  * @method apply
787  */
788 Ext.Template.prototype.apply = Ext.Template.prototype.applyTemplate;
789
790 /**
791  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
792  * @param {String/HTMLElement} el A DOM element or its id
793  * @param {Object} config A configuration object
794  * @return {Ext.Template} The created template
795  * @static
796  */
797 Ext.Template.from = function(el, config){
798     el = Ext.getDom(el);
799     return new Ext.Template(el.value || el.innerHTML, config || '');
800 };/**\r
801  * @class Ext.Template\r
802  */\r
803 Ext.apply(Ext.Template.prototype, {\r
804     /**\r
805      * @cfg {Boolean} disableFormats Specify <tt>true</tt> to disable format\r
806      * functions in the template. If the template does not contain\r
807      * {@link Ext.util.Format format functions}, setting <code>disableFormats</code>\r
808      * to true will reduce <code>{@link #apply}</code> time. Defaults to <tt>false</tt>.\r
809      * <pre><code>\r
810 var t = new Ext.Template(\r
811     '&lt;div name="{id}"&gt;',\r
812         '&lt;span class="{cls}"&gt;{name} {value}&lt;/span&gt;',\r
813     '&lt;/div&gt;',\r
814     {\r
815         compiled: true,      // {@link #compile} immediately\r
816         disableFormats: true // reduce <code>{@link #apply}</code> time since no formatting\r
817     }    \r
818 );\r
819      * </code></pre>\r
820      * For a list of available format functions, see {@link Ext.util.Format}.\r
821      */\r
822     disableFormats : false,                             \r
823     /**\r
824      * See <code>{@link #disableFormats}</code>.\r
825      * @type Boolean\r
826      * @property disableFormats\r
827      */\r
828 \r
829     /**\r
830      * The regular expression used to match template variables\r
831      * @type RegExp\r
832      * @property\r
833      * @hide repeat doc\r
834      */\r
835     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,\r
836 \r
837     /**\r
838      * Returns an HTML fragment of this template with the specified values applied.\r
839      * @param {Object/Array} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})\r
840      * @return {String} The HTML fragment\r
841      * @hide repeat doc\r
842      */\r
843     applyTemplate : function(values){\r
844                 var me = this,\r
845                         useF = me.disableFormats !== true,\r
846                 fm = Ext.util.Format, \r
847                 tpl = me;           \r
848             \r
849         if(me.compiled){\r
850             return me.compiled(values);\r
851         }\r
852         function fn(m, name, format, args){\r
853             if (format && useF) {\r
854                 if (format.substr(0, 5) == "this.") {\r
855                     return tpl.call(format.substr(5), values[name], values);\r
856                 } else {\r
857                     if (args) {\r
858                         // quoted values are required for strings in compiled templates,\r
859                         // but for non compiled we need to strip them\r
860                         // quoted reversed for jsmin\r
861                         var re = /^\s*['"](.*)["']\s*$/;\r
862                         args = args.split(',');\r
863                         for(var i = 0, len = args.length; i < len; i++){\r
864                             args[i] = args[i].replace(re, "$1");\r
865                         }\r
866                         args = [values[name]].concat(args);\r
867                     } else {\r
868                         args = [values[name]];\r
869                     }\r
870                     return fm[format].apply(fm, args);\r
871                 }\r
872             } else {\r
873                 return values[name] !== undefined ? values[name] : "";\r
874             }\r
875         }\r
876         return me.html.replace(me.re, fn);\r
877     },\r
878                 \r
879     /**\r
880      * Compiles the template into an internal function, eliminating the RegEx overhead.\r
881      * @return {Ext.Template} this\r
882      * @hide repeat doc\r
883      */\r
884     compile : function(){\r
885         var me = this,\r
886                 fm = Ext.util.Format,\r
887                 useF = me.disableFormats !== true,\r
888                 sep = Ext.isGecko ? "+" : ",",\r
889                 body;\r
890         \r
891         function fn(m, name, format, args){\r
892             if(format && useF){\r
893                 args = args ? ',' + args : "";\r
894                 if(format.substr(0, 5) != "this."){\r
895                     format = "fm." + format + '(';\r
896                 }else{\r
897                     format = 'this.call("'+ format.substr(5) + '", ';\r
898                     args = ", values";\r
899                 }\r
900             }else{\r
901                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";\r
902             }\r
903             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";\r
904         }\r
905         \r
906         // branched to use + in gecko and [].join() in others\r
907         if(Ext.isGecko){\r
908             body = "this.compiled = function(values){ return '" +\r
909                    me.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +\r
910                     "';};";\r
911         }else{\r
912             body = ["this.compiled = function(values){ return ['"];\r
913             body.push(me.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));\r
914             body.push("'].join('');};");\r
915             body = body.join('');\r
916         }\r
917         eval(body);\r
918         return me;\r
919     },\r
920     \r
921     // private function used to call members\r
922     call : function(fnName, value, allValues){\r
923         return this[fnName](value, allValues);\r
924     }\r
925 });\r
926 Ext.Template.prototype.apply = Ext.Template.prototype.applyTemplate; /*\r
927  * This is code is also distributed under MIT license for use\r
928  * with jQuery and prototype JavaScript libraries.\r
929  */\r
930 /**\r
931  * @class Ext.DomQuery\r
932 Provides high performance selector/xpath processing by compiling queries into reusable functions. New pseudo classes and matchers can be plugged. It works on HTML and XML documents (if a content node is passed in).\r
933 <p>\r
934 DomQuery supports most of the <a href="http://www.w3.org/TR/2005/WD-css3-selectors-20051215/#selectors">CSS3 selectors spec</a>, along with some custom selectors and basic XPath.</p>\r
935 \r
936 <p>\r
937 All selectors, attribute filters and pseudos below can be combined infinitely in any order. For example "div.foo:nth-child(odd)[@foo=bar].bar:first" would be a perfectly valid selector. Node filters are processed in the order in which they appear, which allows you to optimize your queries for your document structure.\r
938 </p>\r
939 <h4>Element Selectors:</h4>\r
940 <ul class="list">\r
941     <li> <b>*</b> any element</li>\r
942     <li> <b>E</b> an element with the tag E</li>\r
943     <li> <b>E F</b> All descendent elements of E that have the tag F</li>\r
944     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>\r
945     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>\r
946     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>\r
947 </ul>\r
948 <h4>Attribute Selectors:</h4>\r
949 <p>The use of &#64; and quotes are optional. For example, div[&#64;foo='bar'] is also a valid attribute selector.</p>\r
950 <ul class="list">\r
951     <li> <b>E[foo]</b> has an attribute "foo"</li>\r
952     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>\r
953     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>\r
954     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>\r
955     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>\r
956     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>\r
957     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>\r
958 </ul>\r
959 <h4>Pseudo Classes:</h4>\r
960 <ul class="list">\r
961     <li> <b>E:first-child</b> E is the first child of its parent</li>\r
962     <li> <b>E:last-child</b> E is the last child of its parent</li>\r
963     <li> <b>E:nth-child(<i>n</i>)</b> E is the <i>n</i>th child of its parent (1 based as per the spec)</li>\r
964     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>\r
965     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>\r
966     <li> <b>E:only-child</b> E is the only child of its parent</li>\r
967     <li> <b>E:checked</b> E is an element that is has a checked attribute that is true (e.g. a radio or checkbox) </li>\r
968     <li> <b>E:first</b> the first E in the resultset</li>\r
969     <li> <b>E:last</b> the last E in the resultset</li>\r
970     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>\r
971     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>\r
972     <li> <b>E:even</b> shortcut for :nth-child(even)</li>\r
973     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>\r
974     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>\r
975     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>\r
976     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>\r
977     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>\r
978     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>\r
979     <li> <b>E:any(S1|S2|S2)</b> an E element which matches any of the simple selectors S1, S2 or S3//\\</li>\r
980 </ul>\r
981 <h4>CSS Value Selectors:</h4>\r
982 <ul class="list">\r
983     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>\r
984     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>\r
985     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>\r
986     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>\r
987     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>\r
988     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>\r
989 </ul>\r
990  * @singleton\r
991  */\r
992 Ext.DomQuery = function(){\r
993     var cache = {}, \r
994         simpleCache = {}, \r
995         valueCache = {},\r
996         nonSpace = /\S/,\r
997         trimRe = /^\s+|\s+$/g,\r
998         tplRe = /\{(\d+)\}/g,\r
999         modeRe = /^(\s?[\/>+~]\s?|\s|$)/,\r
1000         tagTokenRe = /^(#)?([\w-\*]+)/,\r
1001         nthRe = /(\d*)n\+?(\d*)/, \r
1002         nthRe2 = /\D/,\r
1003         // This is for IE MSXML which does not support expandos.\r
1004         // IE runs the same speed using setAttribute, however FF slows way down\r
1005         // and Safari completely fails so they need to continue to use expandos.\r
1006         isIE = window.ActiveXObject ? true : false,\r
1007         key = 30803;\r
1008     \r
1009     // this eval is stop the compressor from\r
1010     // renaming the variable to something shorter\r
1011     eval("var batch = 30803;");         \r
1012 \r
1013     // Retrieve the child node from a particular\r
1014     // parent at the specified index.\r
1015     function child(parent, index){\r
1016         var i = 0,\r
1017             n = parent.firstChild;\r
1018         while(n){\r
1019             if(n.nodeType == 1){\r
1020                if(++i == index){\r
1021                    return n;\r
1022                }\r
1023             }\r
1024             n = n.nextSibling;\r
1025         }\r
1026         return null;\r
1027     }\r
1028 \r
1029     // retrieve the next element node\r
1030     function next(n){   \r
1031         while((n = n.nextSibling) && n.nodeType != 1);\r
1032         return n;\r
1033     }\r
1034 \r
1035     // retrieve the previous element node \r
1036     function prev(n){\r
1037         while((n = n.previousSibling) && n.nodeType != 1);\r
1038         return n;\r
1039     }\r
1040 \r
1041     // Mark each child node with a nodeIndex skipping and\r
1042     // removing empty text nodes.\r
1043     function children(parent){\r
1044         var n = parent.firstChild,\r
1045             nodeIndex = -1,\r
1046             nextNode;\r
1047         while(n){\r
1048             nextNode = n.nextSibling;\r
1049             // clean worthless empty nodes.\r
1050             if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){\r
1051                 parent.removeChild(n);\r
1052             }else{\r
1053                 // add an expando nodeIndex\r
1054                 n.nodeIndex = ++nodeIndex;\r
1055             }\r
1056             n = nextNode;\r
1057         }\r
1058         return this;\r
1059     }\r
1060 \r
1061 \r
1062     // nodeSet - array of nodes\r
1063     // cls - CSS Class\r
1064     function byClassName(nodeSet, cls){\r
1065         if(!cls){\r
1066             return nodeSet;\r
1067         }\r
1068         var result = [], ri = -1;\r
1069         for(var i = 0, ci; ci = nodeSet[i]; i++){\r
1070             if((' '+ci.className+' ').indexOf(cls) != -1){\r
1071                 result[++ri] = ci;\r
1072             }\r
1073         }\r
1074         return result;\r
1075     };\r
1076 \r
1077     function attrValue(n, attr){\r
1078         // if its an array, use the first node.\r
1079         if(!n.tagName && typeof n.length != "undefined"){\r
1080             n = n[0];\r
1081         }\r
1082         if(!n){\r
1083             return null;\r
1084         }\r
1085 \r
1086         if(attr == "for"){\r
1087             return n.htmlFor;\r
1088         }\r
1089         if(attr == "class" || attr == "className"){\r
1090             return n.className;\r
1091         }\r
1092         return n.getAttribute(attr) || n[attr];\r
1093 \r
1094     };\r
1095 \r
1096 \r
1097     // ns - nodes\r
1098     // mode - false, /, >, +, ~\r
1099     // tagName - defaults to "*"\r
1100     function getNodes(ns, mode, tagName){\r
1101         var result = [], ri = -1, cs;\r
1102         if(!ns){\r
1103             return result;\r
1104         }\r
1105         tagName = tagName || "*";\r
1106         // convert to array\r
1107         if(typeof ns.getElementsByTagName != "undefined"){\r
1108             ns = [ns];\r
1109         }\r
1110         \r
1111         // no mode specified, grab all elements by tagName\r
1112         // at any depth\r
1113         if(!mode){\r
1114             for(var i = 0, ni; ni = ns[i]; i++){\r
1115                 cs = ni.getElementsByTagName(tagName);\r
1116                 for(var j = 0, ci; ci = cs[j]; j++){\r
1117                     result[++ri] = ci;\r
1118                 }\r
1119             }\r
1120         // Direct Child mode (/ or >)\r
1121         // E > F or E/F all direct children elements of E that have the tag     \r
1122         } else if(mode == "/" || mode == ">"){\r
1123             var utag = tagName.toUpperCase();\r
1124             for(var i = 0, ni, cn; ni = ns[i]; i++){\r
1125                 cn = ni.childNodes;\r
1126                 for(var j = 0, cj; cj = cn[j]; j++){\r
1127                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){\r
1128                         result[++ri] = cj;\r
1129                     }\r
1130                 }\r
1131             }\r
1132         // Immediately Preceding mode (+)\r
1133         // E + F all elements with the tag F that are immediately preceded by an element with the tag E\r
1134         }else if(mode == "+"){\r
1135             var utag = tagName.toUpperCase();\r
1136             for(var i = 0, n; n = ns[i]; i++){\r
1137                 while((n = n.nextSibling) && n.nodeType != 1);\r
1138                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){\r
1139                     result[++ri] = n;\r
1140                 }\r
1141             }\r
1142         // Sibling mode (~)\r
1143         // E ~ F all elements with the tag F that are preceded by a sibling element with the tag E\r
1144         }else if(mode == "~"){\r
1145             var utag = tagName.toUpperCase();\r
1146             for(var i = 0, n; n = ns[i]; i++){\r
1147                 while((n = n.nextSibling)){\r
1148                     if (n.nodeName == utag || n.nodeName == tagName || tagName == '*'){\r
1149                         result[++ri] = n;\r
1150                     }\r
1151                 }\r
1152             }\r
1153         }\r
1154         return result;\r
1155     }\r
1156 \r
1157     function concat(a, b){\r
1158         if(b.slice){\r
1159             return a.concat(b);\r
1160         }\r
1161         for(var i = 0, l = b.length; i < l; i++){\r
1162             a[a.length] = b[i];\r
1163         }\r
1164         return a;\r
1165     }\r
1166 \r
1167     function byTag(cs, tagName){\r
1168         if(cs.tagName || cs == document){\r
1169             cs = [cs];\r
1170         }\r
1171         if(!tagName){\r
1172             return cs;\r
1173         }\r
1174         var result = [], ri = -1;\r
1175         tagName = tagName.toLowerCase();\r
1176         for(var i = 0, ci; ci = cs[i]; i++){\r
1177             if(ci.nodeType == 1 && ci.tagName.toLowerCase() == tagName){\r
1178                 result[++ri] = ci;\r
1179             }\r
1180         }\r
1181         return result;\r
1182     }\r
1183 \r
1184     function byId(cs, id){\r
1185         if(cs.tagName || cs == document){\r
1186             cs = [cs];\r
1187         }\r
1188         if(!id){\r
1189             return cs;\r
1190         }\r
1191         var result = [], ri = -1;\r
1192         for(var i = 0, ci; ci = cs[i]; i++){\r
1193             if(ci && ci.id == id){\r
1194                 result[++ri] = ci;\r
1195                 return result;\r
1196             }\r
1197         }\r
1198         return result;\r
1199     }\r
1200 \r
1201     // operators are =, !=, ^=, $=, *=, %=, |= and ~=\r
1202     // custom can be "{"\r
1203     function byAttribute(cs, attr, value, op, custom){\r
1204         var result = [], \r
1205             ri = -1, \r
1206             useGetStyle = custom == "{",            \r
1207             fn = Ext.DomQuery.operators[op],        \r
1208             a,      \r
1209             innerHTML;\r
1210         for(var i = 0, ci; ci = cs[i]; i++){\r
1211             // skip non-element nodes.\r
1212             if(ci.nodeType != 1){\r
1213                 continue;\r
1214             }\r
1215             \r
1216             innerHTML = ci.innerHTML;\r
1217             // we only need to change the property names if we're dealing with html nodes, not XML\r
1218             if(innerHTML !== null && innerHTML !== undefined){\r
1219                 if(useGetStyle){\r
1220                     a = Ext.DomQuery.getStyle(ci, attr);\r
1221                 } else if (attr == "class" || attr == "className"){\r
1222                     a = ci.className;\r
1223                 } else if (attr == "for"){\r
1224                     a = ci.htmlFor;\r
1225                 } else if (attr == "href"){\r
1226                     // getAttribute href bug\r
1227                     // http://www.glennjones.net/Post/809/getAttributehrefbug.htm\r
1228                     a = ci.getAttribute("href", 2);\r
1229                 } else{\r
1230                     a = ci.getAttribute(attr);\r
1231                 }\r
1232             }else{\r
1233                 a = ci.getAttribute(attr);\r
1234             }\r
1235             if((fn && fn(a, value)) || (!fn && a)){\r
1236                 result[++ri] = ci;\r
1237             }\r
1238         }\r
1239         return result;\r
1240     }\r
1241 \r
1242     function byPseudo(cs, name, value){\r
1243         return Ext.DomQuery.pseudos[name](cs, value);\r
1244     }\r
1245 \r
1246     function nodupIEXml(cs){\r
1247         var d = ++key, \r
1248             r;\r
1249         cs[0].setAttribute("_nodup", d);\r
1250         r = [cs[0]];\r
1251         for(var i = 1, len = cs.length; i < len; i++){\r
1252             var c = cs[i];\r
1253             if(!c.getAttribute("_nodup") != d){\r
1254                 c.setAttribute("_nodup", d);\r
1255                 r[r.length] = c;\r
1256             }\r
1257         }\r
1258         for(var i = 0, len = cs.length; i < len; i++){\r
1259             cs[i].removeAttribute("_nodup");\r
1260         }\r
1261         return r;\r
1262     }\r
1263 \r
1264     function nodup(cs){\r
1265         if(!cs){\r
1266             return [];\r
1267         }\r
1268         var len = cs.length, c, i, r = cs, cj, ri = -1;\r
1269         if(!len || typeof cs.nodeType != "undefined" || len == 1){\r
1270             return cs;\r
1271         }\r
1272         if(isIE && typeof cs[0].selectSingleNode != "undefined"){\r
1273             return nodupIEXml(cs);\r
1274         }\r
1275         var d = ++key;\r
1276         cs[0]._nodup = d;\r
1277         for(i = 1; c = cs[i]; i++){\r
1278             if(c._nodup != d){\r
1279                 c._nodup = d;\r
1280             }else{\r
1281                 r = [];\r
1282                 for(var j = 0; j < i; j++){\r
1283                     r[++ri] = cs[j];\r
1284                 }\r
1285                 for(j = i+1; cj = cs[j]; j++){\r
1286                     if(cj._nodup != d){\r
1287                         cj._nodup = d;\r
1288                         r[++ri] = cj;\r
1289                     }\r
1290                 }\r
1291                 return r;\r
1292             }\r
1293         }\r
1294         return r;\r
1295     }\r
1296 \r
1297     function quickDiffIEXml(c1, c2){\r
1298         var d = ++key,\r
1299             r = [];\r
1300         for(var i = 0, len = c1.length; i < len; i++){\r
1301             c1[i].setAttribute("_qdiff", d);\r
1302         }        \r
1303         for(var i = 0, len = c2.length; i < len; i++){\r
1304             if(c2[i].getAttribute("_qdiff") != d){\r
1305                 r[r.length] = c2[i];\r
1306             }\r
1307         }\r
1308         for(var i = 0, len = c1.length; i < len; i++){\r
1309            c1[i].removeAttribute("_qdiff");\r
1310         }\r
1311         return r;\r
1312     }\r
1313 \r
1314     function quickDiff(c1, c2){\r
1315         var len1 = c1.length,\r
1316                 d = ++key,\r
1317                 r = [];\r
1318         if(!len1){\r
1319             return c2;\r
1320         }\r
1321         if(isIE && typeof c1[0].selectSingleNode != "undefined"){\r
1322             return quickDiffIEXml(c1, c2);\r
1323         }        \r
1324         for(var i = 0; i < len1; i++){\r
1325             c1[i]._qdiff = d;\r
1326         }        \r
1327         for(var i = 0, len = c2.length; i < len; i++){\r
1328             if(c2[i]._qdiff != d){\r
1329                 r[r.length] = c2[i];\r
1330             }\r
1331         }\r
1332         return r;\r
1333     }\r
1334 \r
1335     function quickId(ns, mode, root, id){\r
1336         if(ns == root){\r
1337            var d = root.ownerDocument || root;\r
1338            return d.getElementById(id);\r
1339         }\r
1340         ns = getNodes(ns, mode, "*");\r
1341         return byId(ns, id);\r
1342     }\r
1343 \r
1344     return {\r
1345         getStyle : function(el, name){\r
1346             return Ext.fly(el).getStyle(name);\r
1347         },\r
1348         /**\r
1349          * Compiles a selector/xpath query into a reusable function. The returned function\r
1350          * takes one parameter "root" (optional), which is the context node from where the query should start.\r
1351          * @param {String} selector The selector/xpath query\r
1352          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match\r
1353          * @return {Function}\r
1354          */\r
1355         compile : function(path, type){\r
1356             type = type || "select";\r
1357 \r
1358             // setup fn preamble\r
1359             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"],\r
1360                 mode,           \r
1361                 lastPath,\r
1362                 matchers = Ext.DomQuery.matchers,\r
1363                 matchersLn = matchers.length,\r
1364                 modeMatch,\r
1365                 // accept leading mode switch\r
1366                 lmode = path.match(modeRe);\r
1367             \r
1368             if(lmode && lmode[1]){\r
1369                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';\r
1370                 path = path.replace(lmode[1], "");\r
1371             }\r
1372             \r
1373             // strip leading slashes\r
1374             while(path.substr(0, 1)=="/"){\r
1375                 path = path.substr(1);\r
1376             }\r
1377 \r
1378             while(path && lastPath != path){\r
1379                 lastPath = path;\r
1380                 var tokenMatch = path.match(tagTokenRe);\r
1381                 if(type == "select"){\r
1382                     if(tokenMatch){\r
1383                         // ID Selector\r
1384                         if(tokenMatch[1] == "#"){\r
1385                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tokenMatch[2]+'");';                 \r
1386                         }else{\r
1387                             fn[fn.length] = 'n = getNodes(n, mode, "'+tokenMatch[2]+'");';\r
1388                         }\r
1389                         path = path.replace(tokenMatch[0], "");\r
1390                     }else if(path.substr(0, 1) != '@'){\r
1391                         fn[fn.length] = 'n = getNodes(n, mode, "*");';\r
1392                     }\r
1393                 // type of "simple"\r
1394                 }else{\r
1395                     if(tokenMatch){\r
1396                         if(tokenMatch[1] == "#"){\r
1397                             fn[fn.length] = 'n = byId(n, "'+tokenMatch[2]+'");';\r
1398                         }else{\r
1399                             fn[fn.length] = 'n = byTag(n, "'+tokenMatch[2]+'");';\r
1400                         }\r
1401                         path = path.replace(tokenMatch[0], "");\r
1402                     }\r
1403                 }\r
1404                 while(!(modeMatch = path.match(modeRe))){\r
1405                     var matched = false;\r
1406                     for(var j = 0; j < matchersLn; j++){\r
1407                         var t = matchers[j];\r
1408                         var m = path.match(t.re);\r
1409                         if(m){\r
1410                             fn[fn.length] = t.select.replace(tplRe, function(x, i){\r
1411                                 return m[i];\r
1412                             });\r
1413                             path = path.replace(m[0], "");\r
1414                             matched = true;\r
1415                             break;\r
1416                         }\r
1417                     }\r
1418                     // prevent infinite loop on bad selector\r
1419                     if(!matched){\r
1420                         throw 'Error parsing selector, parsing failed at "' + path + '"';\r
1421                     }\r
1422                 }\r
1423                 if(modeMatch[1]){\r
1424                     fn[fn.length] = 'mode="'+modeMatch[1].replace(trimRe, "")+'";';\r
1425                     path = path.replace(modeMatch[1], "");\r
1426                 }\r
1427             }\r
1428             // close fn out\r
1429             fn[fn.length] = "return nodup(n);\n}";\r
1430             \r
1431             // eval fn and return it\r
1432             eval(fn.join(""));\r
1433             return f;\r
1434         },\r
1435 \r
1436         /**\r
1437          * Selects a group of elements.\r
1438          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)\r
1439          * @param {Node/String} root (optional) The start of the query (defaults to document).\r
1440          * @return {Array} An Array of DOM elements which match the selector. If there are\r
1441          * no matches, and empty Array is returned.\r
1442          */\r
1443         jsSelect: function(path, root, type){\r
1444             // set root to doc if not specified.\r
1445             root = root || document;\r
1446             \r
1447             if(typeof root == "string"){\r
1448                 root = document.getElementById(root);\r
1449             }\r
1450             var paths = path.split(","),\r
1451                 results = [];\r
1452                 \r
1453             // loop over each selector\r
1454             for(var i = 0, len = paths.length; i < len; i++){           \r
1455                 var subPath = paths[i].replace(trimRe, "");\r
1456                 // compile and place in cache\r
1457                 if(!cache[subPath]){\r
1458                     cache[subPath] = Ext.DomQuery.compile(subPath);\r
1459                     if(!cache[subPath]){\r
1460                         throw subPath + " is not a valid selector";\r
1461                     }\r
1462                 }\r
1463                 var result = cache[subPath](root);\r
1464                 if(result && result != document){\r
1465                     results = results.concat(result);\r
1466                 }\r
1467             }\r
1468             \r
1469             // if there were multiple selectors, make sure dups\r
1470             // are eliminated\r
1471             if(paths.length > 1){\r
1472                 return nodup(results);\r
1473             }\r
1474             return results;\r
1475         },\r
1476         isXml: function(el) {\r
1477             var docEl = (el ? el.ownerDocument || el : 0).documentElement;\r
1478             return docEl ? docEl.nodeName !== "HTML" : false;\r
1479         },\r
1480         select : document.querySelectorAll ? function(path, root, type) {\r
1481             root = root || document;\r
1482             if (!Ext.DomQuery.isXml(root)) {\r
1483                 try {\r
1484                     var cs = root.querySelectorAll(path);\r
1485                     return Ext.toArray(cs);\r
1486                 }\r
1487                 catch (ex) {}           \r
1488             }       \r
1489             return Ext.DomQuery.jsSelect.call(this, path, root, type);\r
1490         } : function(path, root, type) {\r
1491             return Ext.DomQuery.jsSelect.call(this, path, root, type);\r
1492         },\r
1493 \r
1494         /**\r
1495          * Selects a single element.\r
1496          * @param {String} selector The selector/xpath query\r
1497          * @param {Node} root (optional) The start of the query (defaults to document).\r
1498          * @return {Element} The DOM element which matched the selector.\r
1499          */\r
1500         selectNode : function(path, root){\r
1501             return Ext.DomQuery.select(path, root)[0];\r
1502         },\r
1503 \r
1504         /**\r
1505          * Selects the value of a node, optionally replacing null with the defaultValue.\r
1506          * @param {String} selector The selector/xpath query\r
1507          * @param {Node} root (optional) The start of the query (defaults to document).\r
1508          * @param {String} defaultValue\r
1509          * @return {String}\r
1510          */\r
1511         selectValue : function(path, root, defaultValue){\r
1512             path = path.replace(trimRe, "");\r
1513             if(!valueCache[path]){\r
1514                 valueCache[path] = Ext.DomQuery.compile(path, "select");\r
1515             }\r
1516             var n = valueCache[path](root), v;\r
1517             n = n[0] ? n[0] : n;\r
1518                     \r
1519             // overcome a limitation of maximum textnode size\r
1520             // Rumored to potentially crash IE6 but has not been confirmed.\r
1521             // http://reference.sitepoint.com/javascript/Node/normalize\r
1522             // https://developer.mozilla.org/En/DOM/Node.normalize          \r
1523             if (typeof n.normalize == 'function') n.normalize();\r
1524             \r
1525             v = (n && n.firstChild ? n.firstChild.nodeValue : null);\r
1526             return ((v === null||v === undefined||v==='') ? defaultValue : v);\r
1527         },\r
1528 \r
1529         /**\r
1530          * Selects the value of a node, parsing integers and floats. Returns the defaultValue, or 0 if none is specified.\r
1531          * @param {String} selector The selector/xpath query\r
1532          * @param {Node} root (optional) The start of the query (defaults to document).\r
1533          * @param {Number} defaultValue\r
1534          * @return {Number}\r
1535          */\r
1536         selectNumber : function(path, root, defaultValue){\r
1537             var v = Ext.DomQuery.selectValue(path, root, defaultValue || 0);\r
1538             return parseFloat(v);\r
1539         },\r
1540 \r
1541         /**\r
1542          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)\r
1543          * @param {String/HTMLElement/Array} el An element id, element or array of elements\r
1544          * @param {String} selector The simple selector to test\r
1545          * @return {Boolean}\r
1546          */\r
1547         is : function(el, ss){\r
1548             if(typeof el == "string"){\r
1549                 el = document.getElementById(el);\r
1550             }\r
1551             var isArray = Ext.isArray(el),\r
1552                 result = Ext.DomQuery.filter(isArray ? el : [el], ss);\r
1553             return isArray ? (result.length == el.length) : (result.length > 0);\r
1554         },\r
1555 \r
1556         /**\r
1557          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)\r
1558          * @param {Array} el An array of elements to filter\r
1559          * @param {String} selector The simple selector to test\r
1560          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match\r
1561          * the selector instead of the ones that match\r
1562          * @return {Array} An Array of DOM elements which match the selector. If there are\r
1563          * no matches, and empty Array is returned.\r
1564          */\r
1565         filter : function(els, ss, nonMatches){\r
1566             ss = ss.replace(trimRe, "");\r
1567             if(!simpleCache[ss]){\r
1568                 simpleCache[ss] = Ext.DomQuery.compile(ss, "simple");\r
1569             }\r
1570             var result = simpleCache[ss](els);\r
1571             return nonMatches ? quickDiff(result, els) : result;\r
1572         },\r
1573 \r
1574         /**\r
1575          * Collection of matching regular expressions and code snippets.\r
1576          * Each capture group within () will be replace the {} in the select\r
1577          * statement as specified by their index.\r
1578          */\r
1579         matchers : [{\r
1580                 re: /^\.([\w-]+)/,\r
1581                 select: 'n = byClassName(n, " {1} ");'\r
1582             }, {\r
1583                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,\r
1584                 select: 'n = byPseudo(n, "{1}", "{2}");'\r
1585             },{\r
1586                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,\r
1587                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'\r
1588             }, {\r
1589                 re: /^#([\w-]+)/,\r
1590                 select: 'n = byId(n, "{1}");'\r
1591             },{\r
1592                 re: /^@([\w-]+)/,\r
1593                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'\r
1594             }\r
1595         ],\r
1596 \r
1597         /**\r
1598          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.\r
1599          * New operators can be added as long as the match the format <i>c</i>= where <i>c</i> is any character other than space, &gt; &lt;.\r
1600          */\r
1601         operators : {\r
1602             "=" : function(a, v){\r
1603                 return a == v;\r
1604             },\r
1605             "!=" : function(a, v){\r
1606                 return a != v;\r
1607             },\r
1608             "^=" : function(a, v){\r
1609                 return a && a.substr(0, v.length) == v;\r
1610             },\r
1611             "$=" : function(a, v){\r
1612                 return a && a.substr(a.length-v.length) == v;\r
1613             },\r
1614             "*=" : function(a, v){\r
1615                 return a && a.indexOf(v) !== -1;\r
1616             },\r
1617             "%=" : function(a, v){\r
1618                 return (a % v) == 0;\r
1619             },\r
1620             "|=" : function(a, v){\r
1621                 return a && (a == v || a.substr(0, v.length+1) == v+'-');\r
1622             },\r
1623             "~=" : function(a, v){\r
1624                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;\r
1625             }\r
1626         },\r
1627 \r
1628         /**\r
1629          * <p>Object hash of "pseudo class" filter functions which are used when filtering selections. Each function is passed\r
1630          * two parameters:</p><div class="mdetail-params"><ul>\r
1631          * <li><b>c</b> : Array<div class="sub-desc">An Array of DOM elements to filter.</div></li>\r
1632          * <li><b>v</b> : String<div class="sub-desc">The argument (if any) supplied in the selector.</div></li>\r
1633          * </ul></div>\r
1634          * <p>A filter function returns an Array of DOM elements which conform to the pseudo class.</p>\r
1635          * <p>In addition to the provided pseudo classes listed above such as <code>first-child</code> and <code>nth-child</code>,\r
1636          * developers may add additional, custom psuedo class filters to select elements according to application-specific requirements.</p>\r
1637          * <p>For example, to filter <code>&lt;a></code> elements to only return links to <i>external</i> resources:</p>\r
1638          * <code><pre>\r
1639 Ext.DomQuery.pseudos.external = function(c, v){\r
1640     var r = [], ri = -1;\r
1641     for(var i = 0, ci; ci = c[i]; i++){\r
1642 //      Include in result set only if it's a link to an external resource\r
1643         if(ci.hostname != location.hostname){\r
1644             r[++ri] = ci;\r
1645         }\r
1646     }\r
1647     return r;\r
1648 };</pre></code>\r
1649          * Then external links could be gathered with the following statement:<code><pre>\r
1650 var externalLinks = Ext.select("a:external");\r
1651 </code></pre>\r
1652          */\r
1653         pseudos : {\r
1654             "first-child" : function(c){\r
1655                 var r = [], ri = -1, n;\r
1656                 for(var i = 0, ci; ci = n = c[i]; i++){\r
1657                     while((n = n.previousSibling) && n.nodeType != 1);\r
1658                     if(!n){\r
1659                         r[++ri] = ci;\r
1660                     }\r
1661                 }\r
1662                 return r;\r
1663             },\r
1664 \r
1665             "last-child" : function(c){\r
1666                 var r = [], ri = -1, n;\r
1667                 for(var i = 0, ci; ci = n = c[i]; i++){\r
1668                     while((n = n.nextSibling) && n.nodeType != 1);\r
1669                     if(!n){\r
1670                         r[++ri] = ci;\r
1671                     }\r
1672                 }\r
1673                 return r;\r
1674             },\r
1675 \r
1676             "nth-child" : function(c, a) {\r
1677                 var r = [], ri = -1,\r
1678                         m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a),\r
1679                         f = (m[1] || 1) - 0, l = m[2] - 0;\r
1680                 for(var i = 0, n; n = c[i]; i++){\r
1681                     var pn = n.parentNode;\r
1682                     if (batch != pn._batch) {\r
1683                         var j = 0;\r
1684                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){\r
1685                             if(cn.nodeType == 1){\r
1686                                cn.nodeIndex = ++j;\r
1687                             }\r
1688                         }\r
1689                         pn._batch = batch;\r
1690                     }\r
1691                     if (f == 1) {\r
1692                         if (l == 0 || n.nodeIndex == l){\r
1693                             r[++ri] = n;\r
1694                         }\r
1695                     } else if ((n.nodeIndex + l) % f == 0){\r
1696                         r[++ri] = n;\r
1697                     }\r
1698                 }\r
1699 \r
1700                 return r;\r
1701             },\r
1702 \r
1703             "only-child" : function(c){\r
1704                 var r = [], ri = -1;;\r
1705                 for(var i = 0, ci; ci = c[i]; i++){\r
1706                     if(!prev(ci) && !next(ci)){\r
1707                         r[++ri] = ci;\r
1708                     }\r
1709                 }\r
1710                 return r;\r
1711             },\r
1712 \r
1713             "empty" : function(c){\r
1714                 var r = [], ri = -1;\r
1715                 for(var i = 0, ci; ci = c[i]; i++){\r
1716                     var cns = ci.childNodes, j = 0, cn, empty = true;\r
1717                     while(cn = cns[j]){\r
1718                         ++j;\r
1719                         if(cn.nodeType == 1 || cn.nodeType == 3){\r
1720                             empty = false;\r
1721                             break;\r
1722                         }\r
1723                     }\r
1724                     if(empty){\r
1725                         r[++ri] = ci;\r
1726                     }\r
1727                 }\r
1728                 return r;\r
1729             },\r
1730 \r
1731             "contains" : function(c, v){\r
1732                 var r = [], ri = -1;\r
1733                 for(var i = 0, ci; ci = c[i]; i++){\r
1734                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){\r
1735                         r[++ri] = ci;\r
1736                     }\r
1737                 }\r
1738                 return r;\r
1739             },\r
1740 \r
1741             "nodeValue" : function(c, v){\r
1742                 var r = [], ri = -1;\r
1743                 for(var i = 0, ci; ci = c[i]; i++){\r
1744                     if(ci.firstChild && ci.firstChild.nodeValue == v){\r
1745                         r[++ri] = ci;\r
1746                     }\r
1747                 }\r
1748                 return r;\r
1749             },\r
1750 \r
1751             "checked" : function(c){\r
1752                 var r = [], ri = -1;\r
1753                 for(var i = 0, ci; ci = c[i]; i++){\r
1754                     if(ci.checked == true){\r
1755                         r[++ri] = ci;\r
1756                     }\r
1757                 }\r
1758                 return r;\r
1759             },\r
1760 \r
1761             "not" : function(c, ss){\r
1762                 return Ext.DomQuery.filter(c, ss, true);\r
1763             },\r
1764 \r
1765             "any" : function(c, selectors){\r
1766                 var ss = selectors.split('|'),\r
1767                         r = [], ri = -1, s;\r
1768                 for(var i = 0, ci; ci = c[i]; i++){\r
1769                     for(var j = 0; s = ss[j]; j++){\r
1770                         if(Ext.DomQuery.is(ci, s)){\r
1771                             r[++ri] = ci;\r
1772                             break;\r
1773                         }\r
1774                     }\r
1775                 }\r
1776                 return r;\r
1777             },\r
1778 \r
1779             "odd" : function(c){\r
1780                 return this["nth-child"](c, "odd");\r
1781             },\r
1782 \r
1783             "even" : function(c){\r
1784                 return this["nth-child"](c, "even");\r
1785             },\r
1786 \r
1787             "nth" : function(c, a){\r
1788                 return c[a-1] || [];\r
1789             },\r
1790 \r
1791             "first" : function(c){\r
1792                 return c[0] || [];\r
1793             },\r
1794 \r
1795             "last" : function(c){\r
1796                 return c[c.length-1] || [];\r
1797             },\r
1798 \r
1799             "has" : function(c, ss){\r
1800                 var s = Ext.DomQuery.select,\r
1801                         r = [], ri = -1;\r
1802                 for(var i = 0, ci; ci = c[i]; i++){\r
1803                     if(s(ss, ci).length > 0){\r
1804                         r[++ri] = ci;\r
1805                     }\r
1806                 }\r
1807                 return r;\r
1808             },\r
1809 \r
1810             "next" : function(c, ss){\r
1811                 var is = Ext.DomQuery.is,\r
1812                         r = [], ri = -1;\r
1813                 for(var i = 0, ci; ci = c[i]; i++){\r
1814                     var n = next(ci);\r
1815                     if(n && is(n, ss)){\r
1816                         r[++ri] = ci;\r
1817                     }\r
1818                 }\r
1819                 return r;\r
1820             },\r
1821 \r
1822             "prev" : function(c, ss){\r
1823                 var is = Ext.DomQuery.is,\r
1824                         r = [], ri = -1;\r
1825                 for(var i = 0, ci; ci = c[i]; i++){\r
1826                     var n = prev(ci);\r
1827                     if(n && is(n, ss)){\r
1828                         r[++ri] = ci;\r
1829                     }\r
1830                 }\r
1831                 return r;\r
1832             }\r
1833         }\r
1834     };\r
1835 }();\r
1836 \r
1837 /**\r
1838  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Ext.DomQuery#select}\r
1839  * @param {String} path The selector/xpath query\r
1840  * @param {Node} root (optional) The start of the query (defaults to document).\r
1841  * @return {Array}\r
1842  * @member Ext\r
1843  * @method query\r
1844  */\r
1845 Ext.query = Ext.DomQuery.select;\r
1846 /**
1847  * @class Ext.util.DelayedTask
1848  * <p> The DelayedTask class provides a convenient way to "buffer" the execution of a method,
1849  * performing setTimeout where a new timeout cancels the old timeout. When called, the
1850  * task will wait the specified time period before executing. If durng that time period,
1851  * the task is called again, the original call will be cancelled. This continues so that
1852  * the function is only called a single time for each iteration.</p>
1853  * <p>This method is especially useful for things like detecting whether a user has finished
1854  * typing in a text field. An example would be performing validation on a keypress. You can
1855  * use this class to buffer the keypress events for a certain number of milliseconds, and
1856  * perform only if they stop for that amount of time.  Usage:</p><pre><code>
1857 var task = new Ext.util.DelayedTask(function(){
1858     alert(Ext.getDom('myInputField').value.length);
1859 });
1860 // Wait 500ms before calling our function. If the user presses another key 
1861 // during that 500ms, it will be cancelled and we'll wait another 500ms.
1862 Ext.get('myInputField').on('keypress', function(){
1863     task.{@link #delay}(500); 
1864 });
1865  * </code></pre> 
1866  * <p>Note that we are using a DelayedTask here to illustrate a point. The configuration
1867  * option <tt>buffer</tt> for {@link Ext.util.Observable#addListener addListener/on} will
1868  * also setup a delayed task for you to buffer events.</p> 
1869  * @constructor The parameters to this constructor serve as defaults and are not required.
1870  * @param {Function} fn (optional) The default function to call.
1871  * @param {Object} scope The default scope (The <code><b>this</b></code> reference) in which the
1872  * function is called. If not specified, <code>this</code> will refer to the browser window.
1873  * @param {Array} args (optional) The default Array of arguments.
1874  */
1875 Ext.util.DelayedTask = function(fn, scope, args){
1876     var me = this,
1877         id,     
1878         call = function(){
1879                 clearInterval(id);
1880                 id = null;
1881                 fn.apply(scope, args || []);
1882             };
1883             
1884     /**
1885      * Cancels any pending timeout and queues a new one
1886      * @param {Number} delay The milliseconds to delay
1887      * @param {Function} newFn (optional) Overrides function passed to constructor
1888      * @param {Object} newScope (optional) Overrides scope passed to constructor. Remember that if no scope
1889      * is specified, <code>this</code> will refer to the browser window.
1890      * @param {Array} newArgs (optional) Overrides args passed to constructor
1891      */
1892     me.delay = function(delay, newFn, newScope, newArgs){
1893         me.cancel();
1894         fn = newFn || fn;
1895         scope = newScope || scope;
1896         args = newArgs || args;
1897         id = setInterval(call, delay);
1898     };
1899
1900     /**
1901      * Cancel the last queued timeout
1902      */
1903     me.cancel = function(){
1904         if(id){
1905             clearInterval(id);
1906             id = null;
1907         }
1908     };
1909 };(function(){
1910
1911 var EXTUTIL = Ext.util,
1912     TOARRAY = Ext.toArray,
1913     EACH = Ext.each,
1914     ISOBJECT = Ext.isObject,
1915     TRUE = true,
1916     FALSE = false;
1917 /**
1918  * @class Ext.util.Observable
1919  * Base class that provides a common interface for publishing events. Subclasses are expected to
1920  * to have a property "events" with all the events defined, and, optionally, a property "listeners"
1921  * with configured listeners defined.<br>
1922  * For example:
1923  * <pre><code>
1924 Employee = Ext.extend(Ext.util.Observable, {
1925     constructor: function(config){
1926         this.name = config.name;
1927         this.addEvents({
1928             "fired" : true,
1929             "quit" : true
1930         });
1931
1932         // Copy configured listeners into *this* object so that the base class&#39;s
1933         // constructor will add them.
1934         this.listeners = config.listeners;
1935
1936         // Call our superclass constructor to complete construction process.
1937         Employee.superclass.constructor.call(config)
1938     }
1939 });
1940 </code></pre>
1941  * This could then be used like this:<pre><code>
1942 var newEmployee = new Employee({
1943     name: employeeName,
1944     listeners: {
1945         quit: function() {
1946             // By default, "this" will be the object that fired the event.
1947             alert(this.name + " has quit!");
1948         }
1949     }
1950 });
1951 </code></pre>
1952  */
1953 EXTUTIL.Observable = function(){
1954     /**
1955      * @cfg {Object} listeners (optional) <p>A config object containing one or more event handlers to be added to this
1956      * object during initialization.  This should be a valid listeners config object as specified in the
1957      * {@link #addListener} example for attaching multiple handlers at once.</p>
1958      * <br><p><b><u>DOM events from ExtJs {@link Ext.Component Components}</u></b></p>
1959      * <br><p>While <i>some</i> ExtJs Component classes export selected DOM events (e.g. "click", "mouseover" etc), this
1960      * is usually only done when extra value can be added. For example the {@link Ext.DataView DataView}'s
1961      * <b><code>{@link Ext.DataView#click click}</code></b> event passing the node clicked on. To access DOM
1962      * events directly from a Component's HTMLElement, listeners must be added to the <i>{@link Ext.Component#getEl Element}</i> after the Component
1963      * has been rendered. A plugin can simplify this step:<pre><code>
1964 // Plugin is configured with a listeners config object.
1965 // The Component is appended to the argument list of all handler functions.
1966 Ext.DomObserver = Ext.extend(Object, {
1967     constructor: function(config) {
1968         this.listeners = config.listeners ? config.listeners : config;
1969     },
1970
1971     // Component passes itself into plugin&#39;s init method
1972     init: function(c) {
1973         var p, l = this.listeners;
1974         for (p in l) {
1975             if (Ext.isFunction(l[p])) {
1976                 l[p] = this.createHandler(l[p], c);
1977             } else {
1978                 l[p].fn = this.createHandler(l[p].fn, c);
1979             }
1980         }
1981
1982         // Add the listeners to the Element immediately following the render call
1983         c.render = c.render.{@link Function#createSequence createSequence}(function() {
1984             var e = c.getEl();
1985             if (e) {
1986                 e.on(l);
1987             }
1988         });
1989     },
1990
1991     createHandler: function(fn, c) {
1992         return function(e) {
1993             fn.call(this, e, c);
1994         };
1995     }
1996 });
1997
1998 var combo = new Ext.form.ComboBox({
1999
2000     // Collapse combo when its element is clicked on
2001     plugins: [ new Ext.DomObserver({
2002         click: function(evt, comp) {
2003             comp.collapse();
2004         }
2005     })],
2006     store: myStore,
2007     typeAhead: true,
2008     mode: 'local',
2009     triggerAction: 'all'
2010 });
2011      * </code></pre></p>
2012      */
2013     var me = this, e = me.events;
2014     if(me.listeners){
2015         me.on(me.listeners);
2016         delete me.listeners;
2017     }
2018     me.events = e || {};
2019 };
2020
2021 EXTUTIL.Observable.prototype = {
2022     // private
2023     filterOptRe : /^(?:scope|delay|buffer|single)$/,
2024
2025     /**
2026      * <p>Fires the specified event with the passed parameters (minus the event name).</p>
2027      * <p>An event may be set to bubble up an Observable parent hierarchy (See {@link Ext.Component#getBubbleTarget})
2028      * by calling {@link #enableBubble}.</p>
2029      * @param {String} eventName The name of the event to fire.
2030      * @param {Object...} args Variable number of parameters are passed to handlers.
2031      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true.
2032      */
2033     fireEvent : function(){
2034         var a = TOARRAY(arguments),
2035             ename = a[0].toLowerCase(),
2036             me = this,
2037             ret = TRUE,
2038             ce = me.events[ename],
2039             q,
2040             c;
2041         if (me.eventsSuspended === TRUE) {
2042             if (q = me.eventQueue) {
2043                 q.push(a);
2044             }
2045         }
2046         else if(ISOBJECT(ce) && ce.bubble){
2047             if(ce.fire.apply(ce, a.slice(1)) === FALSE) {
2048                 return FALSE;
2049             }
2050             c = me.getBubbleTarget && me.getBubbleTarget();
2051             if(c && c.enableBubble) {
2052                 if(!c.events[ename] || !Ext.isObject(c.events[ename]) || !c.events[ename].bubble) {
2053                     c.enableBubble(ename);
2054                 }
2055                 return c.fireEvent.apply(c, a);
2056             }
2057         }
2058         else {
2059             if (ISOBJECT(ce)) {
2060                 a.shift();
2061                 ret = ce.fire.apply(ce, a);
2062             }
2063         }
2064         return ret;
2065     },
2066
2067     /**
2068      * Appends an event handler to this object.
2069      * @param {String}   eventName The name of the event to listen for.
2070      * @param {Function} handler The method the event invokes.
2071      * @param {Object}   scope (optional) The scope (<code><b>this</b></code> reference) in which the handler function is executed.
2072      * <b>If omitted, defaults to the object which fired the event.</b>
2073      * @param {Object}   options (optional) An object containing handler configuration.
2074      * properties. This may contain any of the following properties:<ul>
2075      * <li><b>scope</b> : Object<div class="sub-desc">The scope (<code><b>this</b></code> reference) in which the handler function is executed.
2076      * <b>If omitted, defaults to the object which fired the event.</b></div></li>
2077      * <li><b>delay</b> : Number<div class="sub-desc">The number of milliseconds to delay the invocation of the handler after the event fires.</div></li>
2078      * <li><b>single</b> : Boolean<div class="sub-desc">True to add a handler to handle just the next firing of the event, and then remove itself.</div></li>
2079      * <li><b>buffer</b> : Number<div class="sub-desc">Causes the handler to be scheduled to run in an {@link Ext.util.DelayedTask} delayed
2080      * by the specified number of milliseconds. If the event fires again within that time, the original
2081      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</div></li>
2082      * <li><b>target</b> : Observable<div class="sub-desc">Only call the handler if the event was fired on the target Observable, <i>not</i>
2083      * if the event was bubbled up from a child Observable.</div></li>
2084      * </ul><br>
2085      * <p>
2086      * <b>Combining Options</b><br>
2087      * Using the options argument, it is possible to combine different types of listeners:<br>
2088      * <br>
2089      * A delayed, one-time listener.
2090      * <pre><code>
2091 myDataView.on('click', this.onClick, this, {
2092 single: true,
2093 delay: 100
2094 });</code></pre>
2095      * <p>
2096      * <b>Attaching multiple handlers in 1 call</b><br>
2097      * The method also allows for a single argument to be passed which is a config object containing properties
2098      * which specify multiple handlers.
2099      * <p>
2100      * <pre><code>
2101 myGridPanel.on({
2102 'click' : {
2103     fn: this.onClick,
2104     scope: this,
2105     delay: 100
2106 },
2107 'mouseover' : {
2108     fn: this.onMouseOver,
2109     scope: this
2110 },
2111 'mouseout' : {
2112     fn: this.onMouseOut,
2113     scope: this
2114 }
2115 });</code></pre>
2116  * <p>
2117  * Or a shorthand syntax:<br>
2118  * <pre><code>
2119 myGridPanel.on({
2120 'click' : this.onClick,
2121 'mouseover' : this.onMouseOver,
2122 'mouseout' : this.onMouseOut,
2123  scope: this
2124 });</code></pre>
2125      */
2126     addListener : function(eventName, fn, scope, o){
2127         var me = this,
2128             e,
2129             oe,
2130             isF,
2131         ce;
2132         if (ISOBJECT(eventName)) {
2133             o = eventName;
2134             for (e in o){
2135                 oe = o[e];
2136                 if (!me.filterOptRe.test(e)) {
2137                     me.addListener(e, oe.fn || oe, oe.scope || o.scope, oe.fn ? oe : o);
2138                 }
2139             }
2140         } else {
2141             eventName = eventName.toLowerCase();
2142             ce = me.events[eventName] || TRUE;
2143             if (Ext.isBoolean(ce)) {
2144                 me.events[eventName] = ce = new EXTUTIL.Event(me, eventName);
2145             }
2146             ce.addListener(fn, scope, ISOBJECT(o) ? o : {});
2147         }
2148     },
2149
2150     /**
2151      * Removes an event handler.
2152      * @param {String}   eventName The type of event the handler was associated with.
2153      * @param {Function} handler   The handler to remove. <b>This must be a reference to the function passed into the {@link #addListener} call.</b>
2154      * @param {Object}   scope     (optional) The scope originally specified for the handler.
2155      */
2156     removeListener : function(eventName, fn, scope){
2157         var ce = this.events[eventName.toLowerCase()];
2158         if (ISOBJECT(ce)) {
2159             ce.removeListener(fn, scope);
2160         }
2161     },
2162
2163     /**
2164      * Removes all listeners for this object
2165      */
2166     purgeListeners : function(){
2167         var events = this.events,
2168             evt,
2169             key;
2170         for(key in events){
2171             evt = events[key];
2172             if(ISOBJECT(evt)){
2173                 evt.clearListeners();
2174             }
2175         }
2176     },
2177
2178     /**
2179      * Adds the specified events to the list of events which this Observable may fire.
2180      * @param {Object|String} o Either an object with event names as properties with a value of <code>true</code>
2181      * or the first event name string if multiple event names are being passed as separate parameters.
2182      * @param {string} Optional. Event name if multiple event names are being passed as separate parameters.
2183      * Usage:<pre><code>
2184 this.addEvents('storeloaded', 'storecleared');
2185 </code></pre>
2186      */
2187     addEvents : function(o){
2188         var me = this;
2189         me.events = me.events || {};
2190         if (Ext.isString(o)) {
2191             var a = arguments,
2192                 i = a.length;
2193             while(i--) {
2194                 me.events[a[i]] = me.events[a[i]] || TRUE;
2195             }
2196         } else {
2197             Ext.applyIf(me.events, o);
2198         }
2199     },
2200
2201     /**
2202      * Checks to see if this object has any listeners for a specified event
2203      * @param {String} eventName The name of the event to check for
2204      * @return {Boolean} True if the event is being listened for, else false
2205      */
2206     hasListener : function(eventName){
2207         var e = this.events[eventName];
2208         return ISOBJECT(e) && e.listeners.length > 0;
2209     },
2210
2211     /**
2212      * Suspend the firing of all events. (see {@link #resumeEvents})
2213      * @param {Boolean} queueSuspended Pass as true to queue up suspended events to be fired
2214      * after the {@link #resumeEvents} call instead of discarding all suspended events;
2215      */
2216     suspendEvents : function(queueSuspended){
2217         this.eventsSuspended = TRUE;
2218         if(queueSuspended && !this.eventQueue){
2219             this.eventQueue = [];
2220         }
2221     },
2222
2223     /**
2224      * Resume firing events. (see {@link #suspendEvents})
2225      * If events were suspended using the <tt><b>queueSuspended</b></tt> parameter, then all
2226      * events fired during event suspension will be sent to any listeners now.
2227      */
2228     resumeEvents : function(){
2229         var me = this,
2230             queued = me.eventQueue || [];
2231         me.eventsSuspended = FALSE;
2232         delete me.eventQueue;
2233         EACH(queued, function(e) {
2234             me.fireEvent.apply(me, e);
2235         });
2236     }
2237 };
2238
2239 var OBSERVABLE = EXTUTIL.Observable.prototype;
2240 /**
2241  * Appends an event handler to this object (shorthand for {@link #addListener}.)
2242  * @param {String}   eventName     The type of event to listen for
2243  * @param {Function} handler       The method the event invokes
2244  * @param {Object}   scope         (optional) The scope (<code><b>this</b></code> reference) in which the handler function is executed.
2245  * <b>If omitted, defaults to the object which fired the event.</b>
2246  * @param {Object}   options       (optional) An object containing handler configuration.
2247  * @method
2248  */
2249 OBSERVABLE.on = OBSERVABLE.addListener;
2250 /**
2251  * Removes an event handler (shorthand for {@link #removeListener}.)
2252  * @param {String}   eventName     The type of event the handler was associated with.
2253  * @param {Function} handler       The handler to remove. <b>This must be a reference to the function passed into the {@link #addListener} call.</b>
2254  * @param {Object}   scope         (optional) The scope originally specified for the handler.
2255  * @method
2256  */
2257 OBSERVABLE.un = OBSERVABLE.removeListener;
2258
2259 /**
2260  * Removes <b>all</b> added captures from the Observable.
2261  * @param {Observable} o The Observable to release
2262  * @static
2263  */
2264 EXTUTIL.Observable.releaseCapture = function(o){
2265     o.fireEvent = OBSERVABLE.fireEvent;
2266 };
2267
2268 function createTargeted(h, o, scope){
2269     return function(){
2270         if(o.target == arguments[0]){
2271             h.apply(scope, TOARRAY(arguments));
2272         }
2273     };
2274 };
2275
2276 function createBuffered(h, o, l, scope){
2277     l.task = new EXTUTIL.DelayedTask();
2278     return function(){
2279         l.task.delay(o.buffer, h, scope, TOARRAY(arguments));
2280     };
2281 };
2282
2283 function createSingle(h, e, fn, scope){
2284     return function(){
2285         e.removeListener(fn, scope);
2286         return h.apply(scope, arguments);
2287     };
2288 };
2289
2290 function createDelayed(h, o, l, scope){
2291     return function(){
2292         var task = new EXTUTIL.DelayedTask();
2293         if(!l.tasks) {
2294             l.tasks = [];
2295         }
2296         l.tasks.push(task);
2297         task.delay(o.delay || 10, h, scope, TOARRAY(arguments));
2298     };
2299 };
2300
2301 EXTUTIL.Event = function(obj, name){
2302     this.name = name;
2303     this.obj = obj;
2304     this.listeners = [];
2305 };
2306
2307 EXTUTIL.Event.prototype = {
2308     addListener : function(fn, scope, options){
2309         var me = this,
2310             l;
2311         scope = scope || me.obj;
2312         if(!me.isListening(fn, scope)){
2313             l = me.createListener(fn, scope, options);
2314             if(me.firing){ // if we are currently firing this event, don't disturb the listener loop
2315                 me.listeners = me.listeners.slice(0);
2316             }
2317             me.listeners.push(l);
2318         }
2319     },
2320
2321     createListener: function(fn, scope, o){
2322         o = o || {}, scope = scope || this.obj;
2323         var l = {
2324             fn: fn,
2325             scope: scope,
2326             options: o
2327         }, h = fn;
2328         if(o.target){
2329             h = createTargeted(h, o, scope);
2330         }
2331         if(o.delay){
2332             h = createDelayed(h, o, l, scope);
2333         }
2334         if(o.single){
2335             h = createSingle(h, this, fn, scope);
2336         }
2337         if(o.buffer){
2338             h = createBuffered(h, o, l, scope);
2339         }
2340         l.fireFn = h;
2341         return l;
2342     },
2343
2344     findListener : function(fn, scope){
2345         var list = this.listeners,
2346             i = list.length,
2347             l,
2348             s;
2349         while(i--) {
2350             l = list[i];
2351             if(l) {
2352                 s = l.scope;
2353                 if(l.fn == fn && (s == scope || s == this.obj)){
2354                     return i;
2355                 }
2356             }
2357         }
2358         return -1;
2359     },
2360
2361     isListening : function(fn, scope){
2362         return this.findListener(fn, scope) != -1;
2363     },
2364
2365     removeListener : function(fn, scope){
2366         var index,
2367             l,
2368             k,
2369             me = this,
2370             ret = FALSE;
2371         if((index = me.findListener(fn, scope)) != -1){
2372             if (me.firing) {
2373                 me.listeners = me.listeners.slice(0);
2374             }
2375             l = me.listeners[index];
2376             if(l.task) {
2377                 l.task.cancel();
2378                 delete l.task;
2379             }
2380             k = l.tasks && l.tasks.length;
2381             if(k) {
2382                 while(k--) {
2383                     l.tasks[k].cancel();
2384                 }
2385                 delete l.tasks;
2386             }
2387             me.listeners.splice(index, 1);
2388             ret = TRUE;
2389         }
2390         return ret;
2391     },
2392
2393     // Iterate to stop any buffered/delayed events
2394     clearListeners : function(){
2395         var me = this,
2396             l = me.listeners,
2397             i = l.length;
2398         while(i--) {
2399             me.removeListener(l[i].fn, l[i].scope);
2400         }
2401     },
2402
2403     fire : function(){
2404         var me = this,
2405             args = TOARRAY(arguments),
2406             listeners = me.listeners,
2407             len = listeners.length,
2408             i = 0,
2409             l;
2410
2411         if(len > 0){
2412             me.firing = TRUE;
2413             for (; i < len; i++) {
2414                 l = listeners[i];
2415                 if(l && l.fireFn.apply(l.scope || me.obj || window, args) === FALSE) {
2416                     return (me.firing = FALSE);
2417                 }
2418             }
2419         }
2420         me.firing = FALSE;
2421         return TRUE;
2422     }
2423 };
2424 })();/**\r
2425  * @class Ext.util.Observable\r
2426  */\r
2427 Ext.apply(Ext.util.Observable.prototype, function(){\r
2428     // this is considered experimental (along with beforeMethod, afterMethod, removeMethodListener?)\r
2429     // allows for easier interceptor and sequences, including cancelling and overwriting the return value of the call\r
2430     // private\r
2431     function getMethodEvent(method){\r
2432         var e = (this.methodEvents = this.methodEvents ||\r
2433         {})[method], returnValue, v, cancel, obj = this;\r
2434 \r
2435         if (!e) {\r
2436             this.methodEvents[method] = e = {};\r
2437             e.originalFn = this[method];\r
2438             e.methodName = method;\r
2439             e.before = [];\r
2440             e.after = [];\r
2441 \r
2442             var makeCall = function(fn, scope, args){\r
2443                 if (!Ext.isEmpty(v = fn.apply(scope || obj, args))) {\r
2444                     if (Ext.isObject(v)) {\r
2445                         returnValue = !Ext.isEmpty(v.returnValue) ? v.returnValue : v;\r
2446                         cancel = !!v.cancel;\r
2447                     }\r
2448                     else\r
2449                         if (v === false) {\r
2450                             cancel = true;\r
2451                         }\r
2452                         else {\r
2453                             returnValue = v;\r
2454                         }\r
2455                 }\r
2456             };\r
2457 \r
2458             this[method] = function(){\r
2459                 var args = Ext.toArray(arguments);\r
2460                 returnValue = v = undefined;\r
2461                 cancel = false;\r
2462 \r
2463                 Ext.each(e.before, function(b){\r
2464                     makeCall(b.fn, b.scope, args);\r
2465                     if (cancel) {\r
2466                         return returnValue;\r
2467                     }\r
2468                 });\r
2469 \r
2470                 if (!Ext.isEmpty(v = e.originalFn.apply(obj, args))) {\r
2471                     returnValue = v;\r
2472                 }\r
2473                 Ext.each(e.after, function(a){\r
2474                     makeCall(a.fn, a.scope, args);\r
2475                     if (cancel) {\r
2476                         return returnValue;\r
2477                     }\r
2478                 });\r
2479                 return returnValue;\r
2480             };\r
2481         }\r
2482         return e;\r
2483     }\r
2484 \r
2485     return {\r
2486         // these are considered experimental\r
2487         // allows for easier interceptor and sequences, including cancelling and overwriting the return value of the call\r
2488         // adds an 'interceptor' called before the original method\r
2489         beforeMethod : function(method, fn, scope){\r
2490             getMethodEvent.call(this, method).before.push({\r
2491                 fn: fn,\r
2492                 scope: scope\r
2493             });\r
2494         },\r
2495 \r
2496         // adds a 'sequence' called after the original method\r
2497         afterMethod : function(method, fn, scope){\r
2498             getMethodEvent.call(this, method).after.push({\r
2499                 fn: fn,\r
2500                 scope: scope\r
2501             });\r
2502         },\r
2503 \r
2504         removeMethodListener: function(method, fn, scope){\r
2505             var e = getMethodEvent.call(this, method), found = false;\r
2506             Ext.each(e.before, function(b, i, arr){\r
2507                 if (b.fn == fn && b.scope == scope) {\r
2508                     arr.splice(i, 1);\r
2509                     found = true;\r
2510                     return false;\r
2511                 }\r
2512             });\r
2513             if (!found) {\r
2514                 Ext.each(e.after, function(a, i, arr){\r
2515                     if (a.fn == fn && a.scope == scope) {\r
2516                         arr.splice(i, 1);\r
2517                         return false;\r
2518                     }\r
2519                 });\r
2520             }\r
2521         },\r
2522 \r
2523         /**\r
2524          * Relays selected events from the specified Observable as if the events were fired by <tt><b>this</b></tt>.\r
2525          * @param {Object} o The Observable whose events this object is to relay.\r
2526          * @param {Array} events Array of event names to relay.\r
2527          */\r
2528         relayEvents : function(o, events){\r
2529             var me = this;\r
2530             function createHandler(ename){\r
2531                 return function(){\r
2532                     return me.fireEvent.apply(me, [ename].concat(Ext.toArray(arguments)));\r
2533                 };\r
2534             }\r
2535             Ext.each(events, function(ename){\r
2536                 me.events[ename] = me.events[ename] || true;\r
2537                 o.on(ename, createHandler(ename), me);\r
2538             });\r
2539         },\r
2540 \r
2541         /**\r
2542          * <p>Enables events fired by this Observable to bubble up an owner hierarchy by calling\r
2543          * <code>this.getBubbleTarget()</code> if present. There is no implementation in the Observable base class.</p>\r
2544          * <p>This is commonly used by Ext.Components to bubble events to owner Containers. See {@link Ext.Component.getBubbleTarget}. The default\r
2545          * implementation in Ext.Component returns the Component's immediate owner. But if a known target is required, this can be overridden to\r
2546          * access the required target more quickly.</p>\r
2547          * <p>Example:</p><pre><code>\r
2548 Ext.override(Ext.form.Field, {\r
2549     //  Add functionality to Field&#39;s initComponent to enable the change event to bubble\r
2550     initComponent : Ext.form.Field.prototype.initComponent.createSequence(function() {\r
2551         this.enableBubble('change');\r
2552     }),\r
2553 \r
2554     //  We know that we want Field&#39;s events to bubble directly to the FormPanel.\r
2555     getBubbleTarget : function() {\r
2556         if (!this.formPanel) {\r
2557             this.formPanel = this.findParentByType('form');\r
2558         }\r
2559         return this.formPanel;\r
2560     }\r
2561 });\r
2562 \r
2563 var myForm = new Ext.formPanel({\r
2564     title: 'User Details',\r
2565     items: [{\r
2566         ...\r
2567     }],\r
2568     listeners: {\r
2569         change: function() {\r
2570             // Title goes red if form has been modified.\r
2571             myForm.header.setStyle('color', 'red');\r
2572         }\r
2573     }\r
2574 });\r
2575 </code></pre>\r
2576          * @param {String/Array} events The event name to bubble, or an Array of event names.\r
2577          */\r
2578         enableBubble : function(events){\r
2579             var me = this;\r
2580             if(!Ext.isEmpty(events)){\r
2581                 events = Ext.isArray(events) ? events : Ext.toArray(arguments);\r
2582                 Ext.each(events, function(ename){\r
2583                     ename = ename.toLowerCase();\r
2584                     var ce = me.events[ename] || true;\r
2585                     if (Ext.isBoolean(ce)) {\r
2586                         ce = new Ext.util.Event(me, ename);\r
2587                         me.events[ename] = ce;\r
2588                     }\r
2589                     ce.bubble = true;\r
2590                 });\r
2591             }\r
2592         }\r
2593     };\r
2594 }());\r
2595 \r
2596 \r
2597 /**\r
2598  * Starts capture on the specified Observable. All events will be passed\r
2599  * to the supplied function with the event name + standard signature of the event\r
2600  * <b>before</b> the event is fired. If the supplied function returns false,\r
2601  * the event will not fire.\r
2602  * @param {Observable} o The Observable to capture events from.\r
2603  * @param {Function} fn The function to call when an event is fired.\r
2604  * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the Observable firing the event.\r
2605  * @static\r
2606  */\r
2607 Ext.util.Observable.capture = function(o, fn, scope){\r
2608     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);\r
2609 };\r
2610 \r
2611 \r
2612 /**\r
2613  * Sets observability on the passed class constructor.<p>\r
2614  * <p>This makes any event fired on any instance of the passed class also fire a single event through\r
2615  * the <i>class</i> allowing for central handling of events on many instances at once.</p>\r
2616  * <p>Usage:</p><pre><code>\r
2617 Ext.util.Observable.observeClass(Ext.data.Connection);\r
2618 Ext.data.Connection.on('beforerequest', function(con, options) {\r
2619     console.log('Ajax request made to ' + options.url);\r
2620 });</code></pre>\r
2621  * @param {Function} c The class constructor to make observable.\r
2622  * @param {Object} listeners An object containing a series of listeners to add. See {@link #addListener}. \r
2623  * @static\r
2624  */\r
2625 Ext.util.Observable.observeClass = function(c, listeners){\r
2626     if(c){\r
2627       if(!c.fireEvent){\r
2628           Ext.apply(c, new Ext.util.Observable());\r
2629           Ext.util.Observable.capture(c.prototype, c.fireEvent, c);\r
2630       }\r
2631       if(Ext.isObject(listeners)){\r
2632           c.on(listeners);\r
2633       }\r
2634       return c;\r
2635    }\r
2636 };/**\r
2637  * @class Ext.EventManager\r
2638  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides\r
2639  * several useful events directly.\r
2640  * See {@link Ext.EventObject} for more details on normalized event objects.\r
2641  * @singleton\r
2642  */\r
2643 Ext.EventManager = function(){\r
2644     var docReadyEvent,\r
2645         docReadyProcId,\r
2646         docReadyState = false,\r
2647         E = Ext.lib.Event,\r
2648         D = Ext.lib.Dom,\r
2649         DOC = document,\r
2650         WINDOW = window,\r
2651         IEDEFERED = "ie-deferred-loader",\r
2652         DOMCONTENTLOADED = "DOMContentLoaded",\r
2653         propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/,\r
2654         /*\r
2655          * This cache is used to hold special js objects, the document and window, that don't have an id. We need to keep\r
2656          * a reference to them so we can look them up at a later point.\r
2657          */\r
2658         specialElCache = [];\r
2659 \r
2660      function getId(el){\r
2661         var id = false,\r
2662             i = 0,\r
2663             len = specialElCache.length,\r
2664             id = false,\r
2665             skip = false,\r
2666             o;\r
2667         if(el){\r
2668             if(el.getElementById || el.navigator){\r
2669                 // look up the id\r
2670                 for(; i < len; ++i){\r
2671                     o = specialElCache[i];\r
2672                     if(o.el === el){\r
2673                         id = o.id;\r
2674                         break;\r
2675                     }\r
2676                 }\r
2677                 if(!id){\r
2678                     // for browsers that support it, ensure that give the el the same id\r
2679                     id = Ext.id(el);\r
2680                     specialElCache.push({\r
2681                         id: id,\r
2682                         el: el\r
2683                     });\r
2684                     skip = true;\r
2685                 }\r
2686             }else{\r
2687                 id = Ext.id(el);\r
2688             }\r
2689             if(!Ext.elCache[id]){\r
2690                 Ext.Element.addToCache(new Ext.Element(el), id);\r
2691                 if(skip){\r
2692                     Ext.elCache[id].skipGC = true;\r
2693                 }\r
2694             }\r
2695         }\r
2696         return id;\r
2697      };\r
2698 \r
2699     /// There is some jquery work around stuff here that isn't needed in Ext Core.\r
2700     function addListener(el, ename, fn, task, wrap, scope){\r
2701         el = Ext.getDom(el);\r
2702         var id = getId(el),\r
2703             es = Ext.elCache[id].events,\r
2704             wfn;\r
2705 \r
2706         wfn = E.on(el, ename, wrap);\r
2707         es[ename] = es[ename] || [];\r
2708 \r
2709         /* 0 = Original Function,\r
2710            1 = Event Manager Wrapped Function,\r
2711            2 = Scope,\r
2712            3 = Adapter Wrapped Function,\r
2713            4 = Buffered Task\r
2714         */\r
2715         es[ename].push([fn, wrap, scope, wfn, task]);\r
2716 \r
2717         // this is a workaround for jQuery and should somehow be removed from Ext Core in the future\r
2718         // without breaking ExtJS.\r
2719 \r
2720         // workaround for jQuery\r
2721         if(el.addEventListener && ename == "mousewheel"){\r
2722             var args = ["DOMMouseScroll", wrap, false];\r
2723             el.addEventListener.apply(el, args);\r
2724             Ext.EventManager.addListener(WINDOW, 'unload', function(){\r
2725                 el.removeEventListener.apply(el, args);\r
2726             });\r
2727         }\r
2728 \r
2729         // fix stopped mousedowns on the document\r
2730         if(el == DOC && ename == "mousedown"){\r
2731             Ext.EventManager.stoppedMouseDownEvent.addListener(wrap);\r
2732         }\r
2733     };\r
2734 \r
2735     function fireDocReady(){\r
2736         if(!docReadyState){\r
2737             Ext.isReady = docReadyState = true;\r
2738             if(docReadyProcId){\r
2739                 clearInterval(docReadyProcId);\r
2740             }\r
2741             if(Ext.isGecko || Ext.isOpera) {\r
2742                 DOC.removeEventListener(DOMCONTENTLOADED, fireDocReady, false);\r
2743             }\r
2744             if(Ext.isIE){\r
2745                 var defer = DOC.getElementById(IEDEFERED);\r
2746                 if(defer){\r
2747                     defer.onreadystatechange = null;\r
2748                     defer.parentNode.removeChild(defer);\r
2749                 }\r
2750             }\r
2751             if(docReadyEvent){\r
2752                 docReadyEvent.fire();\r
2753                 docReadyEvent.listeners = []; // clearListeners no longer compatible.  Force single: true?\r
2754             }\r
2755         }\r
2756     };\r
2757 \r
2758     function initDocReady(){\r
2759         var COMPLETE = "complete";\r
2760 \r
2761         docReadyEvent = new Ext.util.Event();\r
2762         if (Ext.isGecko || Ext.isOpera) {\r
2763             DOC.addEventListener(DOMCONTENTLOADED, fireDocReady, false);\r
2764         } else if (Ext.isIE){\r
2765             DOC.write("<s"+'cript id=' + IEDEFERED + ' defer="defer" src="/'+'/:"></s'+"cript>");\r
2766             DOC.getElementById(IEDEFERED).onreadystatechange = function(){\r
2767                 if(this.readyState == COMPLETE){\r
2768                     fireDocReady();\r
2769                 }\r
2770             };\r
2771         } else if (Ext.isWebKit){\r
2772             docReadyProcId = setInterval(function(){\r
2773                 if(DOC.readyState == COMPLETE) {\r
2774                     fireDocReady();\r
2775                  }\r
2776             }, 10);\r
2777         }\r
2778         // no matter what, make sure it fires on load\r
2779         E.on(WINDOW, "load", fireDocReady);\r
2780     };\r
2781 \r
2782     function createTargeted(h, o){\r
2783         return function(){\r
2784             var args = Ext.toArray(arguments);\r
2785             if(o.target == Ext.EventObject.setEvent(args[0]).target){\r
2786                 h.apply(this, args);\r
2787             }\r
2788         };\r
2789     };\r
2790 \r
2791     function createBuffered(h, o, task){\r
2792         return function(e){\r
2793             // create new event object impl so new events don't wipe out properties\r
2794             task.delay(o.buffer, h, null, [new Ext.EventObjectImpl(e)]);\r
2795         };\r
2796     };\r
2797 \r
2798     function createSingle(h, el, ename, fn, scope){\r
2799         return function(e){\r
2800             Ext.EventManager.removeListener(el, ename, fn, scope);\r
2801             h(e);\r
2802         };\r
2803     };\r
2804 \r
2805     function createDelayed(h, o, fn){\r
2806         return function(e){\r
2807             var task = new Ext.util.DelayedTask(h);\r
2808             if(!fn.tasks) {\r
2809                 fn.tasks = [];\r
2810             }\r
2811             fn.tasks.push(task);\r
2812             task.delay(o.delay || 10, h, null, [new Ext.EventObjectImpl(e)]);\r
2813         };\r
2814     };\r
2815 \r
2816     function listen(element, ename, opt, fn, scope){\r
2817         var o = !Ext.isObject(opt) ? {} : opt,\r
2818             el = Ext.getDom(element), task;\r
2819 \r
2820         fn = fn || o.fn;\r
2821         scope = scope || o.scope;\r
2822 \r
2823         if(!el){\r
2824             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';\r
2825         }\r
2826         function h(e){\r
2827             // prevent errors while unload occurring\r
2828             if(!Ext){// !window[xname]){  ==> can't we do this?\r
2829                 return;\r
2830             }\r
2831             e = Ext.EventObject.setEvent(e);\r
2832             var t;\r
2833             if (o.delegate) {\r
2834                 if(!(t = e.getTarget(o.delegate, el))){\r
2835                     return;\r
2836                 }\r
2837             } else {\r
2838                 t = e.target;\r
2839             }\r
2840             if (o.stopEvent) {\r
2841                 e.stopEvent();\r
2842             }\r
2843             if (o.preventDefault) {\r
2844                e.preventDefault();\r
2845             }\r
2846             if (o.stopPropagation) {\r
2847                 e.stopPropagation();\r
2848             }\r
2849             if (o.normalized) {\r
2850                 e = e.browserEvent;\r
2851             }\r
2852 \r
2853             fn.call(scope || el, e, t, o);\r
2854         };\r
2855         if(o.target){\r
2856             h = createTargeted(h, o);\r
2857         }\r
2858         if(o.delay){\r
2859             h = createDelayed(h, o, fn);\r
2860         }\r
2861         if(o.single){\r
2862             h = createSingle(h, el, ename, fn, scope);\r
2863         }\r
2864         if(o.buffer){\r
2865             task = new Ext.util.DelayedTask(h);\r
2866             h = createBuffered(h, o, task);\r
2867         }\r
2868 \r
2869         addListener(el, ename, fn, task, h, scope);\r
2870         return h;\r
2871     };\r
2872 \r
2873     var pub = {\r
2874         /**\r
2875          * Appends an event handler to an element.  The shorthand version {@link #on} is equivalent.  Typically you will\r
2876          * use {@link Ext.Element#addListener} directly on an Element in favor of calling this version.\r
2877          * @param {String/HTMLElement} el The html element or id to assign the event handler to.\r
2878          * @param {String} eventName The name of the event to listen for.\r
2879          * @param {Function} handler The handler function the event invokes. This function is passed\r
2880          * the following parameters:<ul>\r
2881          * <li>evt : EventObject<div class="sub-desc">The {@link Ext.EventObject EventObject} describing the event.</div></li>\r
2882          * <li>t : Element<div class="sub-desc">The {@link Ext.Element Element} which was the target of the event.\r
2883          * Note that this may be filtered by using the <tt>delegate</tt> option.</div></li>\r
2884          * <li>o : Object<div class="sub-desc">The options object from the addListener call.</div></li>\r
2885          * </ul>\r
2886          * @param {Object} scope (optional) The scope (<b><code>this</code></b> reference) in which the handler function is executed. <b>Defaults to the Element</b>.\r
2887          * @param {Object} options (optional) An object containing handler configuration properties.\r
2888          * This may contain any of the following properties:<ul>\r
2889          * <li>scope : Object<div class="sub-desc">The scope (<b><code>this</code></b> reference) in which the handler function is executed. <b>Defaults to the Element</b>.</div></li>\r
2890          * <li>delegate : String<div class="sub-desc">A simple selector to filter the target or look for a descendant of the target</div></li>\r
2891          * <li>stopEvent : Boolean<div class="sub-desc">True to stop the event. That is stop propagation, and prevent the default action.</div></li>\r
2892          * <li>preventDefault : Boolean<div class="sub-desc">True to prevent the default action</div></li>\r
2893          * <li>stopPropagation : Boolean<div class="sub-desc">True to prevent event propagation</div></li>\r
2894          * <li>normalized : Boolean<div class="sub-desc">False to pass a browser event to the handler function instead of an Ext.EventObject</div></li>\r
2895          * <li>delay : Number<div class="sub-desc">The number of milliseconds to delay the invocation of the handler after te event fires.</div></li>\r
2896          * <li>single : Boolean<div class="sub-desc">True to add a handler to handle just the next firing of the event, and then remove itself.</div></li>\r
2897          * <li>buffer : Number<div class="sub-desc">Causes the handler to be scheduled to run in an {@link Ext.util.DelayedTask} delayed\r
2898          * by the specified number of milliseconds. If the event fires again within that time, the original\r
2899          * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</div></li>\r
2900          * <li>target : Element<div class="sub-desc">Only call the handler if the event was fired on the target Element, <i>not</i> if the event was bubbled up from a child node.</div></li>\r
2901          * </ul><br>\r
2902          * <p>See {@link Ext.Element#addListener} for examples of how to use these options.</p>\r
2903          */\r
2904         addListener : function(element, eventName, fn, scope, options){\r
2905             if(Ext.isObject(eventName)){\r
2906                 var o = eventName, e, val;\r
2907                 for(e in o){\r
2908                     val = o[e];\r
2909                     if(!propRe.test(e)){\r
2910                         if(Ext.isFunction(val)){\r
2911                             // shared options\r
2912                             listen(element, e, o, val, o.scope);\r
2913                         }else{\r
2914                             // individual options\r
2915                             listen(element, e, val);\r
2916                         }\r
2917                     }\r
2918                 }\r
2919             } else {\r
2920                 listen(element, eventName, options, fn, scope);\r
2921             }\r
2922         },\r
2923 \r
2924         /**\r
2925          * Removes an event handler from an element.  The shorthand version {@link #un} is equivalent.  Typically\r
2926          * you will use {@link Ext.Element#removeListener} directly on an Element in favor of calling this version.\r
2927          * @param {String/HTMLElement} el The id or html element from which to remove the listener.\r
2928          * @param {String} eventName The name of the event.\r
2929          * @param {Function} fn The handler function to remove. <b>This must be a reference to the function passed into the {@link #addListener} call.</b>\r
2930          * @param {Object} scope If a scope (<b><code>this</code></b> reference) was specified when the listener was added,\r
2931          * then this must refer to the same object.\r
2932          */\r
2933         removeListener : function(el, eventName, fn, scope){\r
2934             el = Ext.getDom(el);\r
2935             var id = getId(el),\r
2936                 f = el && (Ext.elCache[id].events)[eventName] || [],\r
2937                 wrap, i, l, k, len, fnc;\r
2938 \r
2939             for (i = 0, len = f.length; i < len; i++) {\r
2940 \r
2941                 /* 0 = Original Function,\r
2942                    1 = Event Manager Wrapped Function,\r
2943                    2 = Scope,\r
2944                    3 = Adapter Wrapped Function,\r
2945                    4 = Buffered Task\r
2946                 */\r
2947                 if (Ext.isArray(fnc = f[i]) && fnc[0] == fn && (!scope || fnc[2] == scope)) {\r
2948                     if(fnc[4]) {\r
2949                         fnc[4].cancel();\r
2950                     }\r
2951                     k = fn.tasks && fn.tasks.length;\r
2952                     if(k) {\r
2953                         while(k--) {\r
2954                             fn.tasks[k].cancel();\r
2955                         }\r
2956                         delete fn.tasks;\r
2957                     }\r
2958                     wrap = fnc[1];\r
2959                     E.un(el, eventName, E.extAdapter ? fnc[3] : wrap);\r
2960 \r
2961                     // jQuery workaround that should be removed from Ext Core\r
2962                     if(wrap && el.addEventListener && eventName == "mousewheel"){\r
2963                         el.removeEventListener("DOMMouseScroll", wrap, false);\r
2964                     }\r
2965 \r
2966                     // fix stopped mousedowns on the document\r
2967                     if(wrap && el == DOC && eventName == "mousedown"){\r
2968                         Ext.EventManager.stoppedMouseDownEvent.removeListener(wrap);\r
2969                     }\r
2970 \r
2971                     f.splice(i, 1);\r
2972                     if (f.length === 0) {\r
2973                         delete Ext.elCache[id].events[eventName];\r
2974                     }\r
2975                     for (k in Ext.elCache[id].events) {\r
2976                         return false;\r
2977                     }\r
2978                     Ext.elCache[id].events = {};\r
2979                     return false;\r
2980                 }\r
2981             }\r
2982         },\r
2983 \r
2984         /**\r
2985          * Removes all event handers from an element.  Typically you will use {@link Ext.Element#removeAllListeners}\r
2986          * directly on an Element in favor of calling this version.\r
2987          * @param {String/HTMLElement} el The id or html element from which to remove all event handlers.\r
2988          */\r
2989         removeAll : function(el){\r
2990             el = Ext.getDom(el);\r
2991             var id = getId(el),\r
2992                 ec = Ext.elCache[id] || {},\r
2993                 es = ec.events || {},\r
2994                 f, i, len, ename, fn, k, wrap;\r
2995 \r
2996             for(ename in es){\r
2997                 if(es.hasOwnProperty(ename)){\r
2998                     f = es[ename];\r
2999                     /* 0 = Original Function,\r
3000                        1 = Event Manager Wrapped Function,\r
3001                        2 = Scope,\r
3002                        3 = Adapter Wrapped Function,\r
3003                        4 = Buffered Task\r
3004                     */\r
3005                     for (i = 0, len = f.length; i < len; i++) {\r
3006                         fn = f[i];\r
3007                         if(fn[4]) {\r
3008                             fn[4].cancel();\r
3009                         }\r
3010                         if(fn[0].tasks && (k = fn[0].tasks.length)) {\r
3011                             while(k--) {\r
3012                                 fn[0].tasks[k].cancel();\r
3013                             }\r
3014                             delete fn.tasks;\r
3015                         }\r
3016                         wrap =  fn[1];\r
3017                         E.un(el, ename, E.extAdapter ? fn[3] : wrap);\r
3018 \r
3019                         // jQuery workaround that should be removed from Ext Core\r
3020                         if(el.addEventListener && wrap && ename == "mousewheel"){\r
3021                             el.removeEventListener("DOMMouseScroll", wrap, false);\r
3022                         }\r
3023 \r
3024                         // fix stopped mousedowns on the document\r
3025                         if(wrap && el == DOC &&  ename == "mousedown"){\r
3026                             Ext.EventManager.stoppedMouseDownEvent.removeListener(wrap);\r
3027                         }\r
3028                     }\r
3029                 }\r
3030             }\r
3031             if (Ext.elCache[id]) {\r
3032                 Ext.elCache[id].events = {};\r
3033             }\r
3034         },\r
3035 \r
3036         getListeners : function(el, eventName) {\r
3037             el = Ext.getDom(el);\r
3038             var id = getId(el),\r
3039                 ec = Ext.elCache[id] || {},\r
3040                 es = ec.events || {},\r
3041                 results = [];\r
3042             if (es && es[eventName]) {\r
3043                 return es[eventName];\r
3044             } else {\r
3045                 return null;\r
3046             }\r
3047         },\r
3048 \r
3049         purgeElement : function(el, recurse, eventName) {\r
3050             el = Ext.getDom(el);\r
3051             var id = getId(el),\r
3052                 ec = Ext.elCache[id] || {},\r
3053                 es = ec.events || {},\r
3054                 i, f, len;\r
3055             if (eventName) {\r
3056                 if (es && es.hasOwnProperty(eventName)) {\r
3057                     f = es[eventName];\r
3058                     for (i = 0, len = f.length; i < len; i++) {\r
3059                         Ext.EventManager.removeListener(el, eventName, f[i][0]);\r
3060                     }\r
3061                 }\r
3062             } else {\r
3063                 Ext.EventManager.removeAll(el);\r
3064             }\r
3065             if (recurse && el && el.childNodes) {\r
3066                 for (i = 0, len = el.childNodes.length; i < len; i++) {\r
3067                     Ext.EventManager.purgeElement(el.childNodes[i], recurse, eventName);\r
3068                 }\r
3069             }\r
3070         },\r
3071 \r
3072         _unload : function() {\r
3073             var el;\r
3074             for (el in Ext.elCache) {\r
3075                 Ext.EventManager.removeAll(el);\r
3076             }\r
3077             delete Ext.elCache;\r
3078             delete Ext.Element._flyweights;\r
3079         },\r
3080         /**\r
3081          * Adds a listener to be notified when the document is ready (before onload and before images are loaded). Can be\r
3082          * accessed shorthanded as Ext.onReady().\r
3083          * @param {Function} fn The method the event invokes.\r
3084          * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the handler function executes. Defaults to the browser window.\r
3085          * @param {boolean} options (optional) Options object as passed to {@link Ext.Element#addListener}. It is recommended that the options\r
3086          * <code>{single: true}</code> be used so that the handler is removed on first invocation.\r
3087          */\r
3088         onDocumentReady : function(fn, scope, options){\r
3089             if(docReadyState){ // if it already fired\r
3090                 docReadyEvent.addListener(fn, scope, options);\r
3091                 docReadyEvent.fire();\r
3092                 docReadyEvent.listeners = []; // clearListeners no longer compatible.  Force single: true?\r
3093             } else {\r
3094                 if(!docReadyEvent) initDocReady();\r
3095                 options = options || {};\r
3096                 options.delay = options.delay || 1;\r
3097                 docReadyEvent.addListener(fn, scope, options);\r
3098             }\r
3099         }\r
3100     };\r
3101      /**\r
3102      * Appends an event handler to an element.  Shorthand for {@link #addListener}.\r
3103      * @param {String/HTMLElement} el The html element or id to assign the event handler to\r
3104      * @param {String} eventName The name of the event to listen for.\r
3105      * @param {Function} handler The handler function the event invokes.\r
3106      * @param {Object} scope (optional) (<code>this</code> reference) in which the handler function executes. <b>Defaults to the Element</b>.\r
3107      * @param {Object} options (optional) An object containing standard {@link #addListener} options\r
3108      * @member Ext.EventManager\r
3109      * @method on\r
3110      */\r
3111     pub.on = pub.addListener;\r
3112     /**\r
3113      * Removes an event handler from an element.  Shorthand for {@link #removeListener}.\r
3114      * @param {String/HTMLElement} el The id or html element from which to remove the listener.\r
3115      * @param {String} eventName The name of the event.\r
3116      * @param {Function} fn The handler function to remove. <b>This must be a reference to the function passed into the {@link #on} call.</b>\r
3117      * @param {Object} scope If a scope (<b><code>this</code></b> reference) was specified when the listener was added,\r
3118      * then this must refer to the same object.\r
3119      * @member Ext.EventManager\r
3120      * @method un\r
3121      */\r
3122     pub.un = pub.removeListener;\r
3123 \r
3124     pub.stoppedMouseDownEvent = new Ext.util.Event();\r
3125     return pub;\r
3126 }();\r
3127 /**\r
3128   * Adds a listener to be notified when the document is ready (before onload and before images are loaded). Shorthand of {@link Ext.EventManager#onDocumentReady}.\r
3129   * @param {Function} fn The method the event invokes.\r
3130   * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the handler function executes. Defaults to the browser window.\r
3131   * @param {boolean} options (optional) Options object as passed to {@link Ext.Element#addListener}. It is recommended that the options\r
3132   * <code>{single: true}</code> be used so that the handler is removed on first invocation.\r
3133   * @member Ext\r
3134   * @method onReady\r
3135  */\r
3136 Ext.onReady = Ext.EventManager.onDocumentReady;\r
3137 \r
3138 \r
3139 //Initialize doc classes\r
3140 (function(){\r
3141 \r
3142     var initExtCss = function(){\r
3143         // find the body element\r
3144         var bd = document.body || document.getElementsByTagName('body')[0];\r
3145         if(!bd){ return false; }\r
3146         var cls = [' ',\r
3147                 Ext.isIE ? "ext-ie " + (Ext.isIE6 ? 'ext-ie6' : (Ext.isIE7 ? 'ext-ie7' : 'ext-ie8'))\r
3148                 : Ext.isGecko ? "ext-gecko " + (Ext.isGecko2 ? 'ext-gecko2' : 'ext-gecko3')\r
3149                 : Ext.isOpera ? "ext-opera"\r
3150                 : Ext.isWebKit ? "ext-webkit" : ""];\r
3151 \r
3152         if(Ext.isSafari){\r
3153             cls.push("ext-safari " + (Ext.isSafari2 ? 'ext-safari2' : (Ext.isSafari3 ? 'ext-safari3' : 'ext-safari4')));\r
3154         }else if(Ext.isChrome){\r
3155             cls.push("ext-chrome");\r
3156         }\r
3157 \r
3158         if(Ext.isMac){\r
3159             cls.push("ext-mac");\r
3160         }\r
3161         if(Ext.isLinux){\r
3162             cls.push("ext-linux");\r
3163         }\r
3164 \r
3165         if(Ext.isStrict || Ext.isBorderBox){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"\r
3166             var p = bd.parentNode;\r
3167             if(p){\r
3168                 p.className += Ext.isStrict ? ' ext-strict' : ' ext-border-box';\r
3169             }\r
3170         }\r
3171         bd.className += cls.join(' ');\r
3172         return true;\r
3173     }\r
3174 \r
3175     if(!initExtCss()){\r
3176         Ext.onReady(initExtCss);\r
3177     }\r
3178 })();\r
3179 \r
3180 \r
3181 /**\r
3182  * @class Ext.EventObject\r
3183  * Just as {@link Ext.Element} wraps around a native DOM node, Ext.EventObject\r
3184  * wraps the browser's native event-object normalizing cross-browser differences,\r
3185  * such as which mouse button is clicked, keys pressed, mechanisms to stop\r
3186  * event-propagation along with a method to prevent default actions from taking place.\r
3187  * <p>For example:</p>\r
3188  * <pre><code>\r
3189 function handleClick(e, t){ // e is not a standard event object, it is a Ext.EventObject\r
3190     e.preventDefault();\r
3191     var target = e.getTarget(); // same as t (the target HTMLElement)\r
3192     ...\r
3193 }\r
3194 var myDiv = {@link Ext#get Ext.get}("myDiv");  // get reference to an {@link Ext.Element}\r
3195 myDiv.on(         // 'on' is shorthand for addListener\r
3196     "click",      // perform an action on click of myDiv\r
3197     handleClick   // reference to the action handler\r
3198 );\r
3199 // other methods to do the same:\r
3200 Ext.EventManager.on("myDiv", 'click', handleClick);\r
3201 Ext.EventManager.addListener("myDiv", 'click', handleClick);\r
3202  </code></pre>\r
3203  * @singleton\r
3204  */\r
3205 Ext.EventObject = function(){\r
3206     var E = Ext.lib.Event,\r
3207         // safari keypress events for special keys return bad keycodes\r
3208         safariKeys = {\r
3209             3 : 13, // enter\r
3210             63234 : 37, // left\r
3211             63235 : 39, // right\r
3212             63232 : 38, // up\r
3213             63233 : 40, // down\r
3214             63276 : 33, // page up\r
3215             63277 : 34, // page down\r
3216             63272 : 46, // delete\r
3217             63273 : 36, // home\r
3218             63275 : 35  // end\r
3219         },\r
3220         // normalize button clicks\r
3221         btnMap = Ext.isIE ? {1:0,4:1,2:2} :\r
3222                 (Ext.isWebKit ? {1:0,2:1,3:2} : {0:0,1:1,2:2});\r
3223 \r
3224     Ext.EventObjectImpl = function(e){\r
3225         if(e){\r
3226             this.setEvent(e.browserEvent || e);\r
3227         }\r
3228     };\r
3229 \r
3230     Ext.EventObjectImpl.prototype = {\r
3231            /** @private */\r
3232         setEvent : function(e){\r
3233             var me = this;\r
3234             if(e == me || (e && e.browserEvent)){ // already wrapped\r
3235                 return e;\r
3236             }\r
3237             me.browserEvent = e;\r
3238             if(e){\r
3239                 // normalize buttons\r
3240                 me.button = e.button ? btnMap[e.button] : (e.which ? e.which - 1 : -1);\r
3241                 if(e.type == 'click' && me.button == -1){\r
3242                     me.button = 0;\r
3243                 }\r
3244                 me.type = e.type;\r
3245                 me.shiftKey = e.shiftKey;\r
3246                 // mac metaKey behaves like ctrlKey\r
3247                 me.ctrlKey = e.ctrlKey || e.metaKey || false;\r
3248                 me.altKey = e.altKey;\r
3249                 // in getKey these will be normalized for the mac\r
3250                 me.keyCode = e.keyCode;\r
3251                 me.charCode = e.charCode;\r
3252                 // cache the target for the delayed and or buffered events\r
3253                 me.target = E.getTarget(e);\r
3254                 // same for XY\r
3255                 me.xy = E.getXY(e);\r
3256             }else{\r
3257                 me.button = -1;\r
3258                 me.shiftKey = false;\r
3259                 me.ctrlKey = false;\r
3260                 me.altKey = false;\r
3261                 me.keyCode = 0;\r
3262                 me.charCode = 0;\r
3263                 me.target = null;\r
3264                 me.xy = [0, 0];\r
3265             }\r
3266             return me;\r
3267         },\r
3268 \r
3269         /**\r
3270          * Stop the event (preventDefault and stopPropagation)\r
3271          */\r
3272         stopEvent : function(){\r
3273             var me = this;\r
3274             if(me.browserEvent){\r
3275                 if(me.browserEvent.type == 'mousedown'){\r
3276                     Ext.EventManager.stoppedMouseDownEvent.fire(me);\r
3277                 }\r
3278                 E.stopEvent(me.browserEvent);\r
3279             }\r
3280         },\r
3281 \r
3282         /**\r
3283          * Prevents the browsers default handling of the event.\r
3284          */\r
3285         preventDefault : function(){\r
3286             if(this.browserEvent){\r
3287                 E.preventDefault(this.browserEvent);\r
3288             }\r
3289         },\r
3290 \r
3291         /**\r
3292          * Cancels bubbling of the event.\r
3293          */\r
3294         stopPropagation : function(){\r
3295             var me = this;\r
3296             if(me.browserEvent){\r
3297                 if(me.browserEvent.type == 'mousedown'){\r
3298                     Ext.EventManager.stoppedMouseDownEvent.fire(me);\r
3299                 }\r
3300                 E.stopPropagation(me.browserEvent);\r
3301             }\r
3302         },\r
3303 \r
3304         /**\r
3305          * Gets the character code for the event.\r
3306          * @return {Number}\r
3307          */\r
3308         getCharCode : function(){\r
3309             return this.charCode || this.keyCode;\r
3310         },\r
3311 \r
3312         /**\r
3313          * Returns a normalized keyCode for the event.\r
3314          * @return {Number} The key code\r
3315          */\r
3316         getKey : function(){\r
3317             return this.normalizeKey(this.keyCode || this.charCode)\r
3318         },\r
3319 \r
3320         // private\r
3321         normalizeKey: function(k){\r
3322             return Ext.isSafari ? (safariKeys[k] || k) : k;\r
3323         },\r
3324 \r
3325         /**\r
3326          * Gets the x coordinate of the event.\r
3327          * @return {Number}\r
3328          */\r
3329         getPageX : function(){\r
3330             return this.xy[0];\r
3331         },\r
3332 \r
3333         /**\r
3334          * Gets the y coordinate of the event.\r
3335          * @return {Number}\r
3336          */\r
3337         getPageY : function(){\r
3338             return this.xy[1];\r
3339         },\r
3340 \r
3341         /**\r
3342          * Gets the page coordinates of the event.\r
3343          * @return {Array} The xy values like [x, y]\r
3344          */\r
3345         getXY : function(){\r
3346             return this.xy;\r
3347         },\r
3348 \r
3349         /**\r
3350          * Gets the target for the event.\r
3351          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target\r
3352          * @param {Number/Mixed} maxDepth (optional) The max depth to\r
3353                 search as a number or element (defaults to 10 || document.body)\r
3354          * @param {Boolean} returnEl (optional) True to return a Ext.Element object instead of DOM node\r
3355          * @return {HTMLelement}\r
3356          */\r
3357         getTarget : function(selector, maxDepth, returnEl){\r
3358             return selector ? Ext.fly(this.target).findParent(selector, maxDepth, returnEl) : (returnEl ? Ext.get(this.target) : this.target);\r
3359         },\r
3360 \r
3361         /**\r
3362          * Gets the related target.\r
3363          * @return {HTMLElement}\r
3364          */\r
3365         getRelatedTarget : function(){\r
3366             return this.browserEvent ? E.getRelatedTarget(this.browserEvent) : null;\r
3367         },\r
3368 \r
3369         /**\r
3370          * Normalizes mouse wheel delta across browsers\r
3371          * @return {Number} The delta\r
3372          */\r
3373         getWheelDelta : function(){\r
3374             var e = this.browserEvent;\r
3375             var delta = 0;\r
3376             if(e.wheelDelta){ /* IE/Opera. */\r
3377                 delta = e.wheelDelta/120;\r
3378             }else if(e.detail){ /* Mozilla case. */\r
3379                 delta = -e.detail/3;\r
3380             }\r
3381             return delta;\r
3382         },\r
3383 \r
3384         /**\r
3385         * Returns true if the target of this event is a child of el.  Unless the allowEl parameter is set, it will return false if if the target is el.\r
3386         * Example usage:<pre><code>\r
3387         // Handle click on any child of an element\r
3388         Ext.getBody().on('click', function(e){\r
3389             if(e.within('some-el')){\r
3390                 alert('Clicked on a child of some-el!');\r
3391             }\r
3392         });\r
3393 \r
3394         // Handle click directly on an element, ignoring clicks on child nodes\r
3395         Ext.getBody().on('click', function(e,t){\r
3396             if((t.id == 'some-el') && !e.within(t, true)){\r
3397                 alert('Clicked directly on some-el!');\r
3398             }\r
3399         });\r
3400         </code></pre>\r
3401          * @param {Mixed} el The id, DOM element or Ext.Element to check\r
3402          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target\r
3403          * @param {Boolean} allowEl {optional} true to also check if the passed element is the target or related target\r
3404          * @return {Boolean}\r
3405          */\r
3406         within : function(el, related, allowEl){\r
3407             if(el){\r
3408                 var t = this[related ? "getRelatedTarget" : "getTarget"]();\r
3409                 return t && ((allowEl ? (t == Ext.getDom(el)) : false) || Ext.fly(el).contains(t));\r
3410             }\r
3411             return false;\r
3412         }\r
3413      };\r
3414 \r
3415     return new Ext.EventObjectImpl();\r
3416 }();\r
3417 /**
3418 * @class Ext.EventManager
3419 */
3420 Ext.apply(Ext.EventManager, function(){
3421    var resizeEvent,
3422        resizeTask,
3423        textEvent,
3424        textSize,
3425        D = Ext.lib.Dom,
3426        propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/,
3427        curWidth = 0,
3428        curHeight = 0,
3429        // note 1: IE fires ONLY the keydown event on specialkey autorepeat
3430        // note 2: Safari < 3.1, Gecko (Mac/Linux) & Opera fire only the keypress event on specialkey autorepeat
3431        // (research done by @Jan Wolter at http://unixpapa.com/js/key.html)
3432        useKeydown = Ext.isWebKit ?
3433                    Ext.num(navigator.userAgent.match(/AppleWebKit\/(\d+)/)[1]) >= 525 :
3434                    !((Ext.isGecko && !Ext.isWindows) || Ext.isOpera);
3435
3436    return {
3437        // private
3438        doResizeEvent: function(){
3439            var h = D.getViewHeight(),
3440                w = D.getViewWidth();
3441
3442             //whacky problem in IE where the resize event will fire even though the w/h are the same.
3443             if(curHeight != h || curWidth != w){
3444                resizeEvent.fire(curWidth = w, curHeight = h);
3445             }
3446        },
3447
3448        /**
3449         * Adds a listener to be notified when the browser window is resized and provides resize event buffering (100 milliseconds),
3450         * passes new viewport width and height to handlers.
3451         * @param {Function} fn      The handler function the window resize event invokes.
3452         * @param {Object}   scope   The scope (<code>this</code> reference) in which the handler function executes. Defaults to the browser window.
3453         * @param {boolean}  options Options object as passed to {@link Ext.Element#addListener}
3454         */
3455        onWindowResize : function(fn, scope, options){
3456            if(!resizeEvent){
3457                resizeEvent = new Ext.util.Event();
3458                resizeTask = new Ext.util.DelayedTask(this.doResizeEvent);
3459                Ext.EventManager.on(window, "resize", this.fireWindowResize, this);
3460            }
3461            resizeEvent.addListener(fn, scope, options);
3462        },
3463
3464        // exposed only to allow manual firing
3465        fireWindowResize : function(){
3466            if(resizeEvent){
3467                resizeTask.delay(100);
3468            }
3469        },
3470
3471        /**
3472         * Adds a listener to be notified when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
3473         * @param {Function} fn      The function the event invokes.
3474         * @param {Object}   scope   The scope (<code>this</code> reference) in which the handler function executes. Defaults to the browser window.
3475         * @param {boolean}  options Options object as passed to {@link Ext.Element#addListener}
3476         */
3477        onTextResize : function(fn, scope, options){
3478            if(!textEvent){
3479                textEvent = new Ext.util.Event();
3480                var textEl = new Ext.Element(document.createElement('div'));
3481                textEl.dom.className = 'x-text-resize';
3482                textEl.dom.innerHTML = 'X';
3483                textEl.appendTo(document.body);
3484                textSize = textEl.dom.offsetHeight;
3485                setInterval(function(){
3486                    if(textEl.dom.offsetHeight != textSize){
3487                        textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
3488                    }
3489                }, this.textResizeInterval);
3490            }
3491            textEvent.addListener(fn, scope, options);
3492        },
3493
3494        /**
3495         * Removes the passed window resize listener.
3496         * @param {Function} fn        The method the event invokes
3497         * @param {Object}   scope    The scope of handler
3498         */
3499        removeResizeListener : function(fn, scope){
3500            if(resizeEvent){
3501                resizeEvent.removeListener(fn, scope);
3502            }
3503        },
3504
3505        // private
3506        fireResize : function(){
3507            if(resizeEvent){
3508                resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
3509            }
3510        },
3511
3512         /**
3513         * The frequency, in milliseconds, to check for text resize events (defaults to 50)
3514         */
3515        textResizeInterval : 50,
3516
3517        /**
3518         * Url used for onDocumentReady with using SSL (defaults to Ext.SSL_SECURE_URL)
3519         */
3520        ieDeferSrc : false,
3521
3522        // protected for use inside the framework
3523        // detects whether we should use keydown or keypress based on the browser.
3524        useKeydown: useKeydown
3525    };
3526 }());
3527
3528 Ext.EventManager.on = Ext.EventManager.addListener;
3529
3530
3531 Ext.apply(Ext.EventObjectImpl.prototype, {
3532    /** Key constant @type Number */
3533    BACKSPACE: 8,
3534    /** Key constant @type Number */
3535    TAB: 9,
3536    /** Key constant @type Number */
3537    NUM_CENTER: 12,
3538    /** Key constant @type Number */
3539    ENTER: 13,
3540    /** Key constant @type Number */
3541    RETURN: 13,
3542    /** Key constant @type Number */
3543    SHIFT: 16,
3544    /** Key constant @type Number */
3545    CTRL: 17,
3546    CONTROL : 17, // legacy
3547    /** Key constant @type Number */
3548    ALT: 18,
3549    /** Key constant @type Number */
3550    PAUSE: 19,
3551    /** Key constant @type Number */
3552    CAPS_LOCK: 20,
3553    /** Key constant @type Number */
3554    ESC: 27,
3555    /** Key constant @type Number */
3556    SPACE: 32,
3557    /** Key constant @type Number */
3558    PAGE_UP: 33,
3559    PAGEUP : 33, // legacy
3560    /** Key constant @type Number */
3561    PAGE_DOWN: 34,
3562    PAGEDOWN : 34, // legacy
3563    /** Key constant @type Number */
3564    END: 35,
3565    /** Key constant @type Number */
3566    HOME: 36,
3567    /** Key constant @type Number */
3568    LEFT: 37,
3569    /** Key constant @type Number */
3570    UP: 38,
3571    /** Key constant @type Number */
3572    RIGHT: 39,
3573    /** Key constant @type Number */
3574    DOWN: 40,
3575    /** Key constant @type Number */
3576    PRINT_SCREEN: 44,
3577    /** Key constant @type Number */
3578    INSERT: 45,
3579    /** Key constant @type Number */
3580    DELETE: 46,
3581    /** Key constant @type Number */
3582    ZERO: 48,
3583    /** Key constant @type Number */
3584    ONE: 49,
3585    /** Key constant @type Number */
3586    TWO: 50,
3587    /** Key constant @type Number */
3588    THREE: 51,
3589    /** Key constant @type Number */
3590    FOUR: 52,
3591    /** Key constant @type Number */
3592    FIVE: 53,
3593    /** Key constant @type Number */
3594    SIX: 54,
3595    /** Key constant @type Number */
3596    SEVEN: 55,
3597    /** Key constant @type Number */
3598    EIGHT: 56,
3599    /** Key constant @type Number */
3600    NINE: 57,
3601    /** Key constant @type Number */
3602    A: 65,
3603    /** Key constant @type Number */
3604    B: 66,
3605    /** Key constant @type Number */
3606    C: 67,
3607    /** Key constant @type Number */
3608    D: 68,
3609    /** Key constant @type Number */
3610    E: 69,
3611    /** Key constant @type Number */
3612    F: 70,
3613    /** Key constant @type Number */
3614    G: 71,
3615    /** Key constant @type Number */
3616    H: 72,
3617    /** Key constant @type Number */
3618    I: 73,
3619    /** Key constant @type Number */
3620    J: 74,
3621    /** Key constant @type Number */
3622    K: 75,
3623    /** Key constant @type Number */
3624    L: 76,
3625    /** Key constant @type Number */
3626    M: 77,
3627    /** Key constant @type Number */
3628    N: 78,
3629    /** Key constant @type Number */
3630    O: 79,
3631    /** Key constant @type Number */
3632    P: 80,
3633    /** Key constant @type Number */
3634    Q: 81,
3635    /** Key constant @type Number */
3636    R: 82,
3637    /** Key constant @type Number */
3638    S: 83,
3639    /** Key constant @type Number */
3640    T: 84,
3641    /** Key constant @type Number */
3642    U: 85,
3643    /** Key constant @type Number */
3644    V: 86,
3645    /** Key constant @type Number */
3646    W: 87,
3647    /** Key constant @type Number */
3648    X: 88,
3649    /** Key constant @type Number */
3650    Y: 89,
3651    /** Key constant @type Number */
3652    Z: 90,
3653    /** Key constant @type Number */
3654    CONTEXT_MENU: 93,
3655    /** Key constant @type Number */
3656    NUM_ZERO: 96,
3657    /** Key constant @type Number */
3658    NUM_ONE: 97,
3659    /** Key constant @type Number */
3660    NUM_TWO: 98,
3661    /** Key constant @type Number */
3662    NUM_THREE: 99,
3663    /** Key constant @type Number */
3664    NUM_FOUR: 100,
3665    /** Key constant @type Number */
3666    NUM_FIVE: 101,
3667    /** Key constant @type Number */
3668    NUM_SIX: 102,
3669    /** Key constant @type Number */
3670    NUM_SEVEN: 103,
3671    /** Key constant @type Number */
3672    NUM_EIGHT: 104,
3673    /** Key constant @type Number */
3674    NUM_NINE: 105,
3675    /** Key constant @type Number */
3676    NUM_MULTIPLY: 106,
3677    /** Key constant @type Number */
3678    NUM_PLUS: 107,
3679    /** Key constant @type Number */
3680    NUM_MINUS: 109,
3681    /** Key constant @type Number */
3682    NUM_PERIOD: 110,
3683    /** Key constant @type Number */
3684    NUM_DIVISION: 111,
3685    /** Key constant @type Number */
3686    F1: 112,
3687    /** Key constant @type Number */
3688    F2: 113,
3689    /** Key constant @type Number */
3690    F3: 114,
3691    /** Key constant @type Number */
3692    F4: 115,
3693    /** Key constant @type Number */
3694    F5: 116,
3695    /** Key constant @type Number */
3696    F6: 117,
3697    /** Key constant @type Number */
3698    F7: 118,
3699    /** Key constant @type Number */
3700    F8: 119,
3701    /** Key constant @type Number */
3702    F9: 120,
3703    /** Key constant @type Number */
3704    F10: 121,
3705    /** Key constant @type Number */
3706    F11: 122,
3707    /** Key constant @type Number */
3708    F12: 123,
3709
3710    /** @private */
3711    isNavKeyPress : function(){
3712        var me = this,
3713            k = this.normalizeKey(me.keyCode);
3714        return (k >= 33 && k <= 40) ||  // Page Up/Down, End, Home, Left, Up, Right, Down
3715        k == me.RETURN ||
3716        k == me.TAB ||
3717        k == me.ESC;
3718    },
3719
3720    isSpecialKey : function(){
3721        var k = this.normalizeKey(this.keyCode);
3722        return (this.type == 'keypress' && this.ctrlKey) ||
3723        this.isNavKeyPress() ||
3724        (k == this.BACKSPACE) || // Backspace
3725        (k >= 16 && k <= 20) || // Shift, Ctrl, Alt, Pause, Caps Lock
3726        (k >= 44 && k <= 45);   // Print Screen, Insert
3727    },
3728
3729    getPoint : function(){
3730        return new Ext.lib.Point(this.xy[0], this.xy[1]);
3731    },
3732
3733    /**
3734     * Returns true if the control, meta, shift or alt key was pressed during this event.
3735     * @return {Boolean}
3736     */
3737    hasModifier : function(){
3738        return ((this.ctrlKey || this.altKey) || this.shiftKey);
3739    }
3740 });/**
3741  * @class Ext.Element
3742  * <p>Encapsulates a DOM element, adding simple DOM manipulation facilities, normalizing for browser differences.</p>
3743  * <p>All instances of this class inherit the methods of {@link Ext.Fx} making visual effects easily available to all DOM elements.</p>
3744  * <p>Note that the events documented in this class are not Ext events, they encapsulate browser events. To
3745  * access the underlying browser event, see {@link Ext.EventObject#browserEvent}. Some older
3746  * browsers may not support the full range of events. Which events are supported is beyond the control of ExtJs.</p>
3747  * Usage:<br>
3748 <pre><code>
3749 // by id
3750 var el = Ext.get("my-div");
3751
3752 // by DOM element reference
3753 var el = Ext.get(myDivElement);
3754 </code></pre>
3755  * <b>Animations</b><br />
3756  * <p>When an element is manipulated, by default there is no animation.</p>
3757  * <pre><code>
3758 var el = Ext.get("my-div");
3759
3760 // no animation
3761 el.setWidth(100);
3762  * </code></pre>
3763  * <p>Many of the functions for manipulating an element have an optional "animate" parameter.  This
3764  * parameter can be specified as boolean (<tt>true</tt>) for default animation effects.</p>
3765  * <pre><code>
3766 // default animation
3767 el.setWidth(100, true);
3768  * </code></pre>
3769  *
3770  * <p>To configure the effects, an object literal with animation options to use as the Element animation
3771  * configuration object can also be specified. Note that the supported Element animation configuration
3772  * options are a subset of the {@link Ext.Fx} animation options specific to Fx effects.  The supported
3773  * Element animation configuration options are:</p>
3774 <pre>
3775 Option    Default   Description
3776 --------- --------  ---------------------------------------------
3777 {@link Ext.Fx#duration duration}  .35       The duration of the animation in seconds
3778 {@link Ext.Fx#easing easing}    easeOut   The easing method
3779 {@link Ext.Fx#callback callback}  none      A function to execute when the anim completes
3780 {@link Ext.Fx#scope scope}     this      The scope (this) of the callback function
3781 </pre>
3782  *
3783  * <pre><code>
3784 // Element animation options object
3785 var opt = {
3786     {@link Ext.Fx#duration duration}: 1,
3787     {@link Ext.Fx#easing easing}: 'elasticIn',
3788     {@link Ext.Fx#callback callback}: this.foo,
3789     {@link Ext.Fx#scope scope}: this
3790 };
3791 // animation with some options set
3792 el.setWidth(100, opt);
3793  * </code></pre>
3794  * <p>The Element animation object being used for the animation will be set on the options
3795  * object as "anim", which allows you to stop or manipulate the animation. Here is an example:</p>
3796  * <pre><code>
3797 // using the "anim" property to get the Anim object
3798 if(opt.anim.isAnimated()){
3799     opt.anim.stop();
3800 }
3801  * </code></pre>
3802  * <p>Also see the <tt>{@link #animate}</tt> method for another animation technique.</p>
3803  * <p><b> Composite (Collections of) Elements</b></p>
3804  * <p>For working with collections of Elements, see {@link Ext.CompositeElement}</p>
3805  * @constructor Create a new Element directly.
3806  * @param {String/HTMLElement} element
3807  * @param {Boolean} forceNew (optional) By default the constructor checks to see if there is already an instance of this element in the cache and if there is it returns the same instance. This will skip that check (useful for extending this class).
3808  */
3809 (function(){
3810 var DOC = document;
3811
3812 Ext.Element = function(element, forceNew){
3813     var dom = typeof element == "string" ?
3814               DOC.getElementById(element) : element,
3815         id;
3816
3817     if(!dom) return null;
3818
3819     id = dom.id;
3820
3821     if(!forceNew && id && Ext.elCache[id]){ // element object already exists
3822         return Ext.elCache[id].el;
3823     }
3824
3825     /**
3826      * The DOM element
3827      * @type HTMLElement
3828      */
3829     this.dom = dom;
3830
3831     /**
3832      * The DOM element ID
3833      * @type String
3834      */
3835     this.id = id || Ext.id(dom);
3836 };
3837
3838 var D = Ext.lib.Dom,
3839     DH = Ext.DomHelper,
3840     E = Ext.lib.Event,
3841     A = Ext.lib.Anim,
3842     El = Ext.Element,
3843     EC = Ext.elCache;
3844
3845 El.prototype = {
3846     /**
3847      * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
3848      * @param {Object} o The object with the attributes
3849      * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
3850      * @return {Ext.Element} this
3851      */
3852     set : function(o, useSet){
3853         var el = this.dom,
3854             attr,
3855             val,
3856             useSet = (useSet !== false) && !!el.setAttribute;
3857
3858         for(attr in o){
3859             if (o.hasOwnProperty(attr)) {
3860                 val = o[attr];
3861                 if (attr == 'style') {
3862                     DH.applyStyles(el, val);
3863                 } else if (attr == 'cls') {
3864                     el.className = val;
3865                 } else if (useSet) {
3866                     el.setAttribute(attr, val);
3867                 } else {
3868                     el[attr] = val;
3869                 }
3870             }
3871         }
3872         return this;
3873     },
3874
3875 //  Mouse events
3876     /**
3877      * @event click
3878      * Fires when a mouse click is detected within the element.
3879      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
3880      * @param {HtmlElement} t The target of the event.
3881      * @param {Object} o The options configuration passed to the {@link #addListener} call.
3882      */
3883     /**
3884      * @event contextmenu
3885      * Fires when a right click is detected within the element.
3886      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
3887      * @param {HtmlElement} t The target of the event.
3888      * @param {Object} o The options configuration passed to the {@link #addListener} call.
3889      */
3890     /**
3891      * @event dblclick
3892      * Fires when a mouse double click is detected within the element.
3893      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
3894      * @param {HtmlElement} t The target of the event.
3895      * @param {Object} o The options configuration passed to the {@link #addListener} call.
3896      */
3897     /**
3898      * @event mousedown
3899      * Fires when a mousedown is detected within the element.
3900      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
3901      * @param {HtmlElement} t The target of the event.
3902      * @param {Object} o The options configuration passed to the {@link #addListener} call.
3903      */
3904     /**
3905      * @event mouseup
3906      * Fires when a mouseup is detected within the element.
3907      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
3908      * @param {HtmlElement} t The target of the event.
3909      * @param {Object} o The options configuration passed to the {@link #addListener} call.
3910      */
3911     /**
3912      * @event mouseover
3913      * Fires when a mouseover is detected within the element.
3914      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
3915      * @param {HtmlElement} t The target of the event.
3916      * @param {Object} o The options configuration passed to the {@link #addListener} call.
3917      */
3918     /**
3919      * @event mousemove
3920      * Fires when a mousemove is detected with the element.
3921      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
3922      * @param {HtmlElement} t The target of the event.
3923      * @param {Object} o The options configuration passed to the {@link #addListener} call.
3924      */
3925     /**
3926      * @event mouseout
3927      * Fires when a mouseout is detected with the element.
3928      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
3929      * @param {HtmlElement} t The target of the event.
3930      * @param {Object} o The options configuration passed to the {@link #addListener} call.
3931      */
3932     /**
3933      * @event mouseenter
3934      * Fires when the mouse enters the element.
3935      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
3936      * @param {HtmlElement} t The target of the event.
3937      * @param {Object} o The options configuration passed to the {@link #addListener} call.
3938      */
3939     /**
3940      * @event mouseleave
3941      * Fires when the mouse leaves the element.
3942      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
3943      * @param {HtmlElement} t The target of the event.
3944      * @param {Object} o The options configuration passed to the {@link #addListener} call.
3945      */
3946
3947 //  Keyboard events
3948     /**
3949      * @event keypress
3950      * Fires when a keypress is detected within the element.
3951      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
3952      * @param {HtmlElement} t The target of the event.
3953      * @param {Object} o The options configuration passed to the {@link #addListener} call.
3954      */
3955     /**
3956      * @event keydown
3957      * Fires when a keydown is detected within the element.
3958      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
3959      * @param {HtmlElement} t The target of the event.
3960      * @param {Object} o The options configuration passed to the {@link #addListener} call.
3961      */
3962     /**
3963      * @event keyup
3964      * Fires when a keyup is detected within the element.
3965      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
3966      * @param {HtmlElement} t The target of the event.
3967      * @param {Object} o The options configuration passed to the {@link #addListener} call.
3968      */
3969
3970
3971 //  HTML frame/object events
3972     /**
3973      * @event load
3974      * Fires when the user agent finishes loading all content within the element. Only supported by window, frames, objects and images.
3975      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
3976      * @param {HtmlElement} t The target of the event.
3977      * @param {Object} o The options configuration passed to the {@link #addListener} call.
3978      */
3979     /**
3980      * @event unload
3981      * Fires when the user agent removes all content from a window or frame. For elements, it fires when the target element or any of its content has been removed.
3982      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
3983      * @param {HtmlElement} t The target of the event.
3984      * @param {Object} o The options configuration passed to the {@link #addListener} call.
3985      */
3986     /**
3987      * @event abort
3988      * Fires when an object/image is stopped from loading before completely loaded.
3989      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
3990      * @param {HtmlElement} t The target of the event.
3991      * @param {Object} o The options configuration passed to the {@link #addListener} call.
3992      */
3993     /**
3994      * @event error
3995      * Fires when an object/image/frame cannot be loaded properly.
3996      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
3997      * @param {HtmlElement} t The target of the event.
3998      * @param {Object} o The options configuration passed to the {@link #addListener} call.
3999      */
4000     /**
4001      * @event resize
4002      * Fires when a document view is resized.
4003      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4004      * @param {HtmlElement} t The target of the event.
4005      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4006      */
4007     /**
4008      * @event scroll
4009      * Fires when a document view is scrolled.
4010      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4011      * @param {HtmlElement} t The target of the event.
4012      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4013      */
4014
4015 //  Form events
4016     /**
4017      * @event select
4018      * Fires when a user selects some text in a text field, including input and textarea.
4019      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4020      * @param {HtmlElement} t The target of the event.
4021      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4022      */
4023     /**
4024      * @event change
4025      * Fires when a control loses the input focus and its value has been modified since gaining focus.
4026      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4027      * @param {HtmlElement} t The target of the event.
4028      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4029      */
4030     /**
4031      * @event submit
4032      * Fires when a form is submitted.
4033      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4034      * @param {HtmlElement} t The target of the event.
4035      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4036      */
4037     /**
4038      * @event reset
4039      * Fires when a form is reset.
4040      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4041      * @param {HtmlElement} t The target of the event.
4042      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4043      */
4044     /**
4045      * @event focus
4046      * Fires when an element receives focus either via the pointing device or by tab navigation.
4047      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4048      * @param {HtmlElement} t The target of the event.
4049      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4050      */
4051     /**
4052      * @event blur
4053      * Fires when an element loses focus either via the pointing device or by tabbing navigation.
4054      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4055      * @param {HtmlElement} t The target of the event.
4056      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4057      */
4058
4059 //  User Interface events
4060     /**
4061      * @event DOMFocusIn
4062      * Where supported. Similar to HTML focus event, but can be applied to any focusable element.
4063      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4064      * @param {HtmlElement} t The target of the event.
4065      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4066      */
4067     /**
4068      * @event DOMFocusOut
4069      * Where supported. Similar to HTML blur event, but can be applied to any focusable element.
4070      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4071      * @param {HtmlElement} t The target of the event.
4072      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4073      */
4074     /**
4075      * @event DOMActivate
4076      * Where supported. Fires when an element is activated, for instance, through a mouse click or a keypress.
4077      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4078      * @param {HtmlElement} t The target of the event.
4079      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4080      */
4081
4082 //  DOM Mutation events
4083     /**
4084      * @event DOMSubtreeModified
4085      * Where supported. Fires when the subtree is modified.
4086      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4087      * @param {HtmlElement} t The target of the event.
4088      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4089      */
4090     /**
4091      * @event DOMNodeInserted
4092      * Where supported. Fires when a node has been added as a child of another node.
4093      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4094      * @param {HtmlElement} t The target of the event.
4095      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4096      */
4097     /**
4098      * @event DOMNodeRemoved
4099      * Where supported. Fires when a descendant node of the element is removed.
4100      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4101      * @param {HtmlElement} t The target of the event.
4102      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4103      */
4104     /**
4105      * @event DOMNodeRemovedFromDocument
4106      * Where supported. Fires when a node is being removed from a document.
4107      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4108      * @param {HtmlElement} t The target of the event.
4109      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4110      */
4111     /**
4112      * @event DOMNodeInsertedIntoDocument
4113      * Where supported. Fires when a node is being inserted into a document.
4114      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4115      * @param {HtmlElement} t The target of the event.
4116      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4117      */
4118     /**
4119      * @event DOMAttrModified
4120      * Where supported. Fires when an attribute has been modified.
4121      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4122      * @param {HtmlElement} t The target of the event.
4123      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4124      */
4125     /**
4126      * @event DOMCharacterDataModified
4127      * Where supported. Fires when the character data has been modified.
4128      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4129      * @param {HtmlElement} t The target of the event.
4130      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4131      */
4132
4133     /**
4134      * The default unit to append to CSS values where a unit isn't provided (defaults to px).
4135      * @type String
4136      */
4137     defaultUnit : "px",
4138
4139     /**
4140      * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
4141      * @param {String} selector The simple selector to test
4142      * @return {Boolean} True if this element matches the selector, else false
4143      */
4144     is : function(simpleSelector){
4145         return Ext.DomQuery.is(this.dom, simpleSelector);
4146     },
4147
4148     /**
4149      * Tries to focus the element. Any exceptions are caught and ignored.
4150      * @param {Number} defer (optional) Milliseconds to defer the focus
4151      * @return {Ext.Element} this
4152      */
4153     focus : function(defer, /* private */ dom) {
4154         var me = this,
4155             dom = dom || me.dom;
4156         try{
4157             if(Number(defer)){
4158                 me.focus.defer(defer, null, [null, dom]);
4159             }else{
4160                 dom.focus();
4161             }
4162         }catch(e){}
4163         return me;
4164     },
4165
4166     /**
4167      * Tries to blur the element. Any exceptions are caught and ignored.
4168      * @return {Ext.Element} this
4169      */
4170     blur : function() {
4171         try{
4172             this.dom.blur();
4173         }catch(e){}
4174         return this;
4175     },
4176
4177     /**
4178      * Returns the value of the "value" attribute
4179      * @param {Boolean} asNumber true to parse the value as a number
4180      * @return {String/Number}
4181      */
4182     getValue : function(asNumber){
4183         var val = this.dom.value;
4184         return asNumber ? parseInt(val, 10) : val;
4185     },
4186
4187     /**
4188      * Appends an event handler to this element.  The shorthand version {@link #on} is equivalent.
4189      * @param {String} eventName The name of event to handle.
4190      * @param {Function} fn The handler function the event invokes. This function is passed
4191      * the following parameters:<ul>
4192      * <li><b>evt</b> : EventObject<div class="sub-desc">The {@link Ext.EventObject EventObject} describing the event.</div></li>
4193      * <li><b>el</b> : HtmlElement<div class="sub-desc">The DOM element which was the target of the event.
4194      * Note that this may be filtered by using the <tt>delegate</tt> option.</div></li>
4195      * <li><b>o</b> : Object<div class="sub-desc">The options object from the addListener call.</div></li>
4196      * </ul>
4197      * @param {Object} scope (optional) The scope (<code><b>this</b></code> reference) in which the handler function is executed.
4198      * <b>If omitted, defaults to this Element.</b>.
4199      * @param {Object} options (optional) An object containing handler configuration properties.
4200      * This may contain any of the following properties:<ul>
4201      * <li><b>scope</b> Object : <div class="sub-desc">The scope (<code><b>this</b></code> reference) in which the handler function is executed.
4202      * <b>If omitted, defaults to this Element.</b></div></li>
4203      * <li><b>delegate</b> String: <div class="sub-desc">A simple selector to filter the target or look for a descendant of the target. See below for additional details.</div></li>
4204      * <li><b>stopEvent</b> Boolean: <div class="sub-desc">True to stop the event. That is stop propagation, and prevent the default action.</div></li>
4205      * <li><b>preventDefault</b> Boolean: <div class="sub-desc">True to prevent the default action</div></li>
4206      * <li><b>stopPropagation</b> Boolean: <div class="sub-desc">True to prevent event propagation</div></li>
4207      * <li><b>normalized</b> Boolean: <div class="sub-desc">False to pass a browser event to the handler function instead of an Ext.EventObject</div></li>
4208      * <li><b>target</b> Ext.Element: <div class="sub-desc">Only call the handler if the event was fired on the target Element, <i>not</i> if the event was bubbled up from a child node.</div></li>
4209      * <li><b>delay</b> Number: <div class="sub-desc">The number of milliseconds to delay the invocation of the handler after the event fires.</div></li>
4210      * <li><b>single</b> Boolean: <div class="sub-desc">True to add a handler to handle just the next firing of the event, and then remove itself.</div></li>
4211      * <li><b>buffer</b> Number: <div class="sub-desc">Causes the handler to be scheduled to run in an {@link Ext.util.DelayedTask} delayed
4212      * by the specified number of milliseconds. If the event fires again within that time, the original
4213      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</div></li>
4214      * </ul><br>
4215      * <p>
4216      * <b>Combining Options</b><br>
4217      * In the following examples, the shorthand form {@link #on} is used rather than the more verbose
4218      * addListener.  The two are equivalent.  Using the options argument, it is possible to combine different
4219      * types of listeners:<br>
4220      * <br>
4221      * A delayed, one-time listener that auto stops the event and adds a custom argument (forumId) to the
4222      * options object. The options object is available as the third parameter in the handler function.<div style="margin: 5px 20px 20px;">
4223      * Code:<pre><code>
4224 el.on('click', this.onClick, this, {
4225     single: true,
4226     delay: 100,
4227     stopEvent : true,
4228     forumId: 4
4229 });</code></pre></p>
4230      * <p>
4231      * <b>Attaching multiple handlers in 1 call</b><br>
4232      * The method also allows for a single argument to be passed which is a config object containing properties
4233      * which specify multiple handlers.</p>
4234      * <p>
4235      * Code:<pre><code>
4236 el.on({
4237     'click' : {
4238         fn: this.onClick,
4239         scope: this,
4240         delay: 100
4241     },
4242     'mouseover' : {
4243         fn: this.onMouseOver,
4244         scope: this
4245     },
4246     'mouseout' : {
4247         fn: this.onMouseOut,
4248         scope: this
4249     }
4250 });</code></pre>
4251      * <p>
4252      * Or a shorthand syntax:<br>
4253      * Code:<pre><code></p>
4254 el.on({
4255     'click' : this.onClick,
4256     'mouseover' : this.onMouseOver,
4257     'mouseout' : this.onMouseOut,
4258     scope: this
4259 });
4260      * </code></pre></p>
4261      * <p><b>delegate</b></p>
4262      * <p>This is a configuration option that you can pass along when registering a handler for
4263      * an event to assist with event delegation. Event delegation is a technique that is used to
4264      * reduce memory consumption and prevent exposure to memory-leaks. By registering an event
4265      * for a container element as opposed to each element within a container. By setting this
4266      * configuration option to a simple selector, the target element will be filtered to look for
4267      * a descendant of the target.
4268      * For example:<pre><code>
4269 // using this markup:
4270 &lt;div id='elId'>
4271     &lt;p id='p1'>paragraph one&lt;/p>
4272     &lt;p id='p2' class='clickable'>paragraph two&lt;/p>
4273     &lt;p id='p3'>paragraph three&lt;/p>
4274 &lt;/div>
4275 // utilize event delegation to registering just one handler on the container element:
4276 el = Ext.get('elId');
4277 el.on(
4278     'click',
4279     function(e,t) {
4280         // handle click
4281         console.info(t.id); // 'p2'
4282     },
4283     this,
4284     {
4285         // filter the target element to be a descendant with the class 'clickable'
4286         delegate: '.clickable'
4287     }
4288 );
4289      * </code></pre></p>
4290      * @return {Ext.Element} this
4291      */
4292     addListener : function(eventName, fn, scope, options){
4293         Ext.EventManager.on(this.dom,  eventName, fn, scope || this, options);
4294         return this;
4295     },
4296
4297     /**
4298      * Removes an event handler from this element.  The shorthand version {@link #un} is equivalent.
4299      * <b>Note</b>: if a <i>scope</i> was explicitly specified when {@link #addListener adding} the
4300      * listener, the same scope must be specified here.
4301      * Example:
4302      * <pre><code>
4303 el.removeListener('click', this.handlerFn);
4304 // or
4305 el.un('click', this.handlerFn);
4306 </code></pre>
4307      * @param {String} eventName The name of the event from which to remove the handler.
4308      * @param {Function} fn The handler function to remove. <b>This must be a reference to the function passed into the {@link #addListener} call.</b>
4309      * @param {Object} scope If a scope (<b><code>this</code></b> reference) was specified when the listener was added,
4310      * then this must refer to the same object.
4311      * @return {Ext.Element} this
4312      */
4313     removeListener : function(eventName, fn, scope){
4314         Ext.EventManager.removeListener(this.dom,  eventName, fn, scope || this);
4315         return this;
4316     },
4317
4318     /**
4319      * Removes all previous added listeners from this element
4320      * @return {Ext.Element} this
4321      */
4322     removeAllListeners : function(){
4323         Ext.EventManager.removeAll(this.dom);
4324         return this;
4325     },
4326
4327     /**
4328      * Recursively removes all previous added listeners from this element and its children
4329      * @return {Ext.Element} this
4330      */
4331     purgeAllListeners : function() {
4332         Ext.EventManager.purgeElement(this, true);
4333         return this;
4334     },
4335     /**
4336      * @private Test if size has a unit, otherwise appends the default
4337      */
4338     addUnits : function(size){
4339         if(size === "" || size == "auto" || size === undefined){
4340             size = size || '';
4341         } else if(!isNaN(size) || !unitPattern.test(size)){
4342             size = size + (this.defaultUnit || 'px');
4343         }
4344         return size;
4345     },
4346
4347     /**
4348      * <p>Updates the <a href="http://developer.mozilla.org/en/DOM/element.innerHTML">innerHTML</a> of this Element
4349      * from a specified URL. Note that this is subject to the <a href="http://en.wikipedia.org/wiki/Same_origin_policy">Same Origin Policy</a></p>
4350      * <p>Updating innerHTML of an element will <b>not</b> execute embedded <tt>&lt;script></tt> elements. This is a browser restriction.</p>
4351      * @param {Mixed} options. Either a sring containing the URL from which to load the HTML, or an {@link Ext.Ajax#request} options object specifying
4352      * exactly how to request the HTML.
4353      * @return {Ext.Element} this
4354      */
4355     load : function(url, params, cb){
4356         Ext.Ajax.request(Ext.apply({
4357             params: params,
4358             url: url.url || url,
4359             callback: cb,
4360             el: this.dom,
4361             indicatorText: url.indicatorText || ''
4362         }, Ext.isObject(url) ? url : {}));
4363         return this;
4364     },
4365
4366     /**
4367      * Tests various css rules/browsers to determine if this element uses a border box
4368      * @return {Boolean}
4369      */
4370     isBorderBox : function(){
4371         return noBoxAdjust[(this.dom.tagName || "").toLowerCase()] || Ext.isBorderBox;
4372     },
4373
4374     /**
4375      * <p>Removes this element's dom reference.  Note that event and cache removal is handled at {@link Ext#removeNode}</p>
4376      */
4377     remove : function(){
4378         var me = this,
4379             dom = me.dom;
4380
4381         if (dom) {
4382             delete me.dom;
4383             Ext.removeNode(dom);
4384         }
4385     },
4386
4387     /**
4388      * Sets up event handlers to call the passed functions when the mouse is moved into and out of the Element.
4389      * @param {Function} overFn The function to call when the mouse enters the Element.
4390      * @param {Function} outFn The function to call when the mouse leaves the Element.
4391      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the functions are executed. Defaults to the Element's DOM element.
4392      * @param {Object} options (optional) Options for the listener. See {@link Ext.util.Observable#addListener the <tt>options</tt> parameter}.
4393      * @return {Ext.Element} this
4394      */
4395     hover : function(overFn, outFn, scope, options){
4396         var me = this;
4397         me.on('mouseenter', overFn, scope || me.dom, options);
4398         me.on('mouseleave', outFn, scope || me.dom, options);
4399         return me;
4400     },
4401
4402     /**
4403      * Returns true if this element is an ancestor of the passed element
4404      * @param {HTMLElement/String} el The element to check
4405      * @return {Boolean} True if this element is an ancestor of el, else false
4406      */
4407     contains : function(el){
4408         return !el ? false : Ext.lib.Dom.isAncestor(this.dom, el.dom ? el.dom : el);
4409     },
4410
4411     /**
4412      * Returns the value of a namespaced attribute from the element's underlying DOM node.
4413      * @param {String} namespace The namespace in which to look for the attribute
4414      * @param {String} name The attribute name
4415      * @return {String} The attribute value
4416      * @deprecated
4417      */
4418     getAttributeNS : function(ns, name){
4419         return this.getAttribute(name, ns);
4420     },
4421
4422     /**
4423      * Returns the value of an attribute from the element's underlying DOM node.
4424      * @param {String} name The attribute name
4425      * @param {String} namespace (optional) The namespace in which to look for the attribute
4426      * @return {String} The attribute value
4427      */
4428     getAttribute : Ext.isIE ? function(name, ns){
4429         var d = this.dom,
4430             type = typeof d[ns + ":" + name];
4431
4432         if(['undefined', 'unknown'].indexOf(type) == -1){
4433             return d[ns + ":" + name];
4434         }
4435         return d[name];
4436     } : function(name, ns){
4437         var d = this.dom;
4438         return d.getAttributeNS(ns, name) || d.getAttribute(ns + ":" + name) || d.getAttribute(name) || d[name];
4439     },
4440
4441     /**
4442     * Update the innerHTML of this element
4443     * @param {String} html The new HTML
4444     * @return {Ext.Element} this
4445      */
4446     update : function(html) {
4447         if (this.dom) {
4448             this.dom.innerHTML = html;
4449         }
4450         return this;
4451     }
4452 };
4453
4454 var ep = El.prototype;
4455
4456 El.addMethods = function(o){
4457    Ext.apply(ep, o);
4458 };
4459
4460 /**
4461  * Appends an event handler (shorthand for {@link #addListener}).
4462  * @param {String} eventName The name of event to handle.
4463  * @param {Function} fn The handler function the event invokes.
4464  * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the handler function is executed.
4465  * @param {Object} options (optional) An object containing standard {@link #addListener} options
4466  * @member Ext.Element
4467  * @method on
4468  */
4469 ep.on = ep.addListener;
4470
4471 /**
4472  * Removes an event handler from this element (see {@link #removeListener} for additional notes).
4473  * @param {String} eventName The name of the event from which to remove the handler.
4474  * @param {Function} fn The handler function to remove. <b>This must be a reference to the function passed into the {@link #addListener} call.</b>
4475  * @param {Object} scope If a scope (<b><code>this</code></b> reference) was specified when the listener was added,
4476  * then this must refer to the same object.
4477  * @return {Ext.Element} this
4478  * @member Ext.Element
4479  * @method un
4480  */
4481 ep.un = ep.removeListener;
4482
4483 /**
4484  * true to automatically adjust width and height settings for box-model issues (default to true)
4485  */
4486 ep.autoBoxAdjust = true;
4487
4488 // private
4489 var unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i,
4490     docEl;
4491
4492 /**
4493  * @private
4494  */
4495
4496 /**
4497  * Retrieves Ext.Element objects.
4498  * <p><b>This method does not retrieve {@link Ext.Component Component}s.</b> This method
4499  * retrieves Ext.Element objects which encapsulate DOM elements. To retrieve a Component by
4500  * its ID, use {@link Ext.ComponentMgr#get}.</p>
4501  * <p>Uses simple caching to consistently return the same object. Automatically fixes if an
4502  * object was recreated with the same id via AJAX or DOM.</p>
4503  * @param {Mixed} el The id of the node, a DOM Node or an existing Element.
4504  * @return {Element} The Element object (or null if no matching element was found)
4505  * @static
4506  * @member Ext.Element
4507  * @method get
4508  */
4509 El.get = function(el){
4510     var ex,
4511         elm,
4512         id;
4513     if(!el){ return null; }
4514     if (typeof el == "string") { // element id
4515         if (!(elm = DOC.getElementById(el))) {
4516             return null;
4517         }
4518         if (EC[el] && EC[el].el) {
4519             ex = EC[el].el;
4520             ex.dom = elm;
4521         } else {
4522             ex = El.addToCache(new El(elm));
4523         }
4524         return ex;
4525     } else if (el.tagName) { // dom element
4526         if(!(id = el.id)){
4527             id = Ext.id(el);
4528         }
4529         if (EC[id] && EC[id].el) {
4530             ex = EC[id].el;
4531             ex.dom = el;
4532         } else {
4533             ex = El.addToCache(new El(el));
4534         }
4535         return ex;
4536     } else if (el instanceof El) {
4537         if(el != docEl){
4538             el.dom = DOC.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
4539                                                           // catch case where it hasn't been appended
4540         }
4541         return el;
4542     } else if(el.isComposite) {
4543         return el;
4544     } else if(Ext.isArray(el)) {
4545         return El.select(el);
4546     } else if(el == DOC) {
4547         // create a bogus element object representing the document object
4548         if(!docEl){
4549             var f = function(){};
4550             f.prototype = El.prototype;
4551             docEl = new f();
4552             docEl.dom = DOC;
4553         }
4554         return docEl;
4555     }
4556     return null;
4557 };
4558
4559 El.addToCache = function(el, id){
4560     id = id || el.id;
4561     EC[id] = {
4562         el:  el,
4563         data: {},
4564         events: {}
4565     };
4566     return el;
4567 };
4568
4569 // private method for getting and setting element data
4570 El.data = function(el, key, value){
4571     el = El.get(el);
4572     if (!el) {
4573         return null;
4574     }
4575     var c = EC[el.id].data;
4576     if(arguments.length == 2){
4577         return c[key];
4578     }else{
4579         return (c[key] = value);
4580     }
4581 };
4582
4583 // private
4584 // Garbage collection - uncache elements/purge listeners on orphaned elements
4585 // so we don't hold a reference and cause the browser to retain them
4586 function garbageCollect(){
4587     if(!Ext.enableGarbageCollector){
4588         clearInterval(El.collectorThreadId);
4589     } else {
4590         var eid,
4591             el,
4592             d,
4593             o;
4594
4595         for(eid in EC){
4596             o = EC[eid];
4597             if(o.skipGC){
4598                 continue;
4599             }
4600             el = o.el;
4601             d = el.dom;
4602             // -------------------------------------------------------
4603             // Determining what is garbage:
4604             // -------------------------------------------------------
4605             // !d
4606             // dom node is null, definitely garbage
4607             // -------------------------------------------------------
4608             // !d.parentNode
4609             // no parentNode == direct orphan, definitely garbage
4610             // -------------------------------------------------------
4611             // !d.offsetParent && !document.getElementById(eid)
4612             // display none elements have no offsetParent so we will
4613             // also try to look it up by it's id. However, check
4614             // offsetParent first so we don't do unneeded lookups.
4615             // This enables collection of elements that are not orphans
4616             // directly, but somewhere up the line they have an orphan
4617             // parent.
4618             // -------------------------------------------------------
4619             if(!d || !d.parentNode || (!d.offsetParent && !DOC.getElementById(eid))){
4620                 if(Ext.enableListenerCollection){
4621                     Ext.EventManager.removeAll(d);
4622                 }
4623                 delete EC[eid];
4624             }
4625         }
4626         // Cleanup IE Object leaks
4627         if (Ext.isIE) {
4628             var t = {};
4629             for (eid in EC) {
4630                 t[eid] = EC[eid];
4631             }
4632             EC = Ext.elCache = t;
4633         }
4634     }
4635 }
4636 El.collectorThreadId = setInterval(garbageCollect, 30000);
4637
4638 var flyFn = function(){};
4639 flyFn.prototype = El.prototype;
4640
4641 // dom is optional
4642 El.Flyweight = function(dom){
4643     this.dom = dom;
4644 };
4645
4646 El.Flyweight.prototype = new flyFn();
4647 El.Flyweight.prototype.isFlyweight = true;
4648 El._flyweights = {};
4649
4650 /**
4651  * <p>Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
4652  * the dom node can be overwritten by other code. Shorthand of {@link Ext.Element#fly}</p>
4653  * <p>Use this to make one-time references to DOM elements which are not going to be accessed again either by
4654  * application code, or by Ext's classes. If accessing an element which will be processed regularly, then {@link Ext#get}
4655  * will be more appropriate to take advantage of the caching provided by the Ext.Element class.</p>
4656  * @param {String/HTMLElement} el The dom node or id
4657  * @param {String} named (optional) Allows for creation of named reusable flyweights to prevent conflicts
4658  * (e.g. internally Ext uses "_global")
4659  * @return {Element} The shared Element object (or null if no matching element was found)
4660  * @member Ext.Element
4661  * @method fly
4662  */
4663 El.fly = function(el, named){
4664     var ret = null;
4665     named = named || '_global';
4666
4667     if (el = Ext.getDom(el)) {
4668         (El._flyweights[named] = El._flyweights[named] || new El.Flyweight()).dom = el;
4669         ret = El._flyweights[named];
4670     }
4671     return ret;
4672 };
4673
4674 /**
4675  * Retrieves Ext.Element objects.
4676  * <p><b>This method does not retrieve {@link Ext.Component Component}s.</b> This method
4677  * retrieves Ext.Element objects which encapsulate DOM elements. To retrieve a Component by
4678  * its ID, use {@link Ext.ComponentMgr#get}.</p>
4679  * <p>Uses simple caching to consistently return the same object. Automatically fixes if an
4680  * object was recreated with the same id via AJAX or DOM.</p>
4681  * Shorthand of {@link Ext.Element#get}
4682  * @param {Mixed} el The id of the node, a DOM Node or an existing Element.
4683  * @return {Element} The Element object (or null if no matching element was found)
4684  * @member Ext
4685  * @method get
4686  */
4687 Ext.get = El.get;
4688
4689 /**
4690  * <p>Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
4691  * the dom node can be overwritten by other code. Shorthand of {@link Ext.Element#fly}</p>
4692  * <p>Use this to make one-time references to DOM elements which are not going to be accessed again either by
4693  * application code, or by Ext's classes. If accessing an element which will be processed regularly, then {@link Ext#get}
4694  * will be more appropriate to take advantage of the caching provided by the Ext.Element class.</p>
4695  * @param {String/HTMLElement} el The dom node or id
4696  * @param {String} named (optional) Allows for creation of named reusable flyweights to prevent conflicts
4697  * (e.g. internally Ext uses "_global")
4698  * @return {Element} The shared Element object (or null if no matching element was found)
4699  * @member Ext
4700  * @method fly
4701  */
4702 Ext.fly = El.fly;
4703
4704 // speedy lookup for elements never to box adjust
4705 var noBoxAdjust = Ext.isStrict ? {
4706     select:1
4707 } : {
4708     input:1, select:1, textarea:1
4709 };
4710 if(Ext.isIE || Ext.isGecko){
4711     noBoxAdjust['button'] = 1;
4712 }
4713
4714 })();
4715 /**
4716  * @class Ext.Element
4717  */
4718 Ext.Element.addMethods({
4719     /**
4720      * Stops the specified event(s) from bubbling and optionally prevents the default action
4721      * @param {String/Array} eventName an event / array of events to stop from bubbling
4722      * @param {Boolean} preventDefault (optional) true to prevent the default action too
4723      * @return {Ext.Element} this
4724      */
4725     swallowEvent : function(eventName, preventDefault){
4726         var me = this;
4727         function fn(e){
4728             e.stopPropagation();
4729             if(preventDefault){
4730                 e.preventDefault();
4731             }
4732         }
4733         if(Ext.isArray(eventName)){
4734             Ext.each(eventName, function(e) {
4735                  me.on(e, fn);
4736             });
4737             return me;
4738         }
4739         me.on(eventName, fn);
4740         return me;
4741     },
4742
4743     /**
4744      * Create an event handler on this element such that when the event fires and is handled by this element,
4745      * it will be relayed to another object (i.e., fired again as if it originated from that object instead).
4746      * @param {String} eventName The type of event to relay
4747      * @param {Object} object Any object that extends {@link Ext.util.Observable} that will provide the context
4748      * for firing the relayed event
4749      */
4750     relayEvent : function(eventName, observable){
4751         this.on(eventName, function(e){
4752             observable.fireEvent(eventName, e);
4753         });
4754     },
4755
4756     /**
4757      * Removes worthless text nodes
4758      * @param {Boolean} forceReclean (optional) By default the element
4759      * keeps track if it has been cleaned already so
4760      * you can call this over and over. However, if you update the element and
4761      * need to force a reclean, you can pass true.
4762      */
4763     clean : function(forceReclean){
4764         var me = this,
4765             dom = me.dom,
4766             n = dom.firstChild,
4767             ni = -1;
4768
4769         if(Ext.Element.data(dom, 'isCleaned') && forceReclean !== true){
4770             return me;
4771         }
4772
4773         while(n){
4774             var nx = n.nextSibling;
4775             if(n.nodeType == 3 && !/\S/.test(n.nodeValue)){
4776                 dom.removeChild(n);
4777             }else{
4778                 n.nodeIndex = ++ni;
4779             }
4780             n = nx;
4781         }
4782         Ext.Element.data(dom, 'isCleaned', true);
4783         return me;
4784     },
4785
4786     /**
4787      * Direct access to the Updater {@link Ext.Updater#update} method. The method takes the same object
4788      * parameter as {@link Ext.Updater#update}
4789      * @return {Ext.Element} this
4790      */
4791     load : function(){
4792         var um = this.getUpdater();
4793         um.update.apply(um, arguments);
4794         return this;
4795     },
4796
4797     /**
4798     * Gets this element's {@link Ext.Updater Updater}
4799     * @return {Ext.Updater} The Updater
4800     */
4801     getUpdater : function(){
4802         return this.updateManager || (this.updateManager = new Ext.Updater(this));
4803     },
4804
4805     /**
4806     * Update the innerHTML of this element, optionally searching for and processing scripts
4807     * @param {String} html The new HTML
4808     * @param {Boolean} loadScripts (optional) True to look for and process scripts (defaults to false)
4809     * @param {Function} callback (optional) For async script loading you can be notified when the update completes
4810     * @return {Ext.Element} this
4811      */
4812     update : function(html, loadScripts, callback){
4813         if (!this.dom) {
4814             return this;
4815         }
4816         html = html || "";
4817
4818         if(loadScripts !== true){
4819             this.dom.innerHTML = html;
4820             if(Ext.isFunction(callback)){
4821                 callback();
4822             }
4823             return this;
4824         }
4825
4826         var id = Ext.id(),
4827             dom = this.dom;
4828
4829         html += '<span id="' + id + '"></span>';
4830
4831         Ext.lib.Event.onAvailable(id, function(){
4832             var DOC = document,
4833                 hd = DOC.getElementsByTagName("head")[0],
4834                 re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig,
4835                 srcRe = /\ssrc=([\'\"])(.*?)\1/i,
4836                 typeRe = /\stype=([\'\"])(.*?)\1/i,
4837                 match,
4838                 attrs,
4839                 srcMatch,
4840                 typeMatch,
4841                 el,
4842                 s;
4843
4844             while((match = re.exec(html))){
4845                 attrs = match[1];
4846                 srcMatch = attrs ? attrs.match(srcRe) : false;
4847                 if(srcMatch && srcMatch[2]){
4848                    s = DOC.createElement("script");
4849                    s.src = srcMatch[2];
4850                    typeMatch = attrs.match(typeRe);
4851                    if(typeMatch && typeMatch[2]){
4852                        s.type = typeMatch[2];
4853                    }
4854                    hd.appendChild(s);
4855                 }else if(match[2] && match[2].length > 0){
4856                     if(window.execScript) {
4857                        window.execScript(match[2]);
4858                     } else {
4859                        window.eval(match[2]);
4860                     }
4861                 }
4862             }
4863             el = DOC.getElementById(id);
4864             if(el){Ext.removeNode(el);}
4865             if(Ext.isFunction(callback)){
4866                 callback();
4867             }
4868         });
4869         dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
4870         return this;
4871     },
4872
4873     // inherit docs, overridden so we can add removeAnchor
4874     removeAllListeners : function(){
4875         this.removeAnchor();
4876         Ext.EventManager.removeAll(this.dom);
4877         return this;
4878     },
4879
4880     /**
4881      * Creates a proxy element of this element
4882      * @param {String/Object} config The class name of the proxy element or a DomHelper config object
4883      * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
4884      * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
4885      * @return {Ext.Element} The new proxy element
4886      */
4887     createProxy : function(config, renderTo, matchBox){
4888         config = Ext.isObject(config) ? config : {tag : "div", cls: config};
4889
4890         var me = this,
4891             proxy = renderTo ? Ext.DomHelper.append(renderTo, config, true) :
4892                                Ext.DomHelper.insertBefore(me.dom, config, true);
4893
4894         if(matchBox && me.setBox && me.getBox){ // check to make sure Element.position.js is loaded
4895            proxy.setBox(me.getBox());
4896         }
4897         return proxy;
4898     }
4899 });
4900
4901 Ext.Element.prototype.getUpdateManager = Ext.Element.prototype.getUpdater;
4902 /**
4903  * @class Ext.Element
4904  */
4905 Ext.Element.addMethods({
4906     /**
4907      * Gets the x,y coordinates specified by the anchor position on the element.
4908      * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo}
4909      * for details on supported anchor positions.
4910      * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead
4911      * of page coordinates
4912      * @param {Object} size (optional) An object containing the size to use for calculating anchor position
4913      * {width: (target width), height: (target height)} (defaults to the element's current size)
4914      * @return {Array} [x, y] An array containing the element's x and y coordinates
4915      */
4916     getAnchorXY : function(anchor, local, s){
4917         //Passing a different size is useful for pre-calculating anchors,
4918         //especially for anchored animations that change the el size.
4919                 anchor = (anchor || "tl").toLowerCase();
4920         s = s || {};
4921         
4922         var me = this,        
4923                 vp = me.dom == document.body || me.dom == document,
4924                 w = s.width || vp ? Ext.lib.Dom.getViewWidth() : me.getWidth(),
4925                 h = s.height || vp ? Ext.lib.Dom.getViewHeight() : me.getHeight(),                              
4926                 xy,             
4927                 r = Math.round,
4928                 o = me.getXY(),
4929                 scroll = me.getScroll(),
4930                 extraX = vp ? scroll.left : !local ? o[0] : 0,
4931                 extraY = vp ? scroll.top : !local ? o[1] : 0,
4932                 hash = {
4933                         c  : [r(w * 0.5), r(h * 0.5)],
4934                         t  : [r(w * 0.5), 0],
4935                         l  : [0, r(h * 0.5)],
4936                         r  : [w, r(h * 0.5)],
4937                         b  : [r(w * 0.5), h],
4938                         tl : [0, 0],    
4939                         bl : [0, h],
4940                         br : [w, h],
4941                         tr : [w, 0]
4942                 };
4943         
4944         xy = hash[anchor];      
4945         return [xy[0] + extraX, xy[1] + extraY]; 
4946     },
4947
4948     /**
4949      * Anchors an element to another element and realigns it when the window is resized.
4950      * @param {Mixed} element The element to align to.
4951      * @param {String} position The position to align to.
4952      * @param {Array} offsets (optional) Offset the positioning by [x, y]
4953      * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
4954      * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
4955      * is a number, it is used as the buffer delay (defaults to 50ms).
4956      * @param {Function} callback The function to call after the animation finishes
4957      * @return {Ext.Element} this
4958      */
4959     anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){        
4960             var me = this,
4961             dom = me.dom,
4962             scroll = !Ext.isEmpty(monitorScroll),
4963             action = function(){
4964                 Ext.fly(dom).alignTo(el, alignment, offsets, animate);
4965                 Ext.callback(callback, Ext.fly(dom));
4966             },
4967             anchor = this.getAnchor();
4968             
4969         // previous listener anchor, remove it
4970         this.removeAnchor();
4971         Ext.apply(anchor, {
4972             fn: action,
4973             scroll: scroll
4974         });
4975
4976         Ext.EventManager.onWindowResize(action, null);
4977         
4978         if(scroll){
4979             Ext.EventManager.on(window, 'scroll', action, null,
4980                 {buffer: !isNaN(monitorScroll) ? monitorScroll : 50});
4981         }
4982         action.call(me); // align immediately
4983         return me;
4984     },
4985     
4986     /**
4987      * Remove any anchor to this element. See {@link #anchorTo}.
4988      * @return {Ext.Element} this
4989      */
4990     removeAnchor : function(){
4991         var me = this,
4992             anchor = this.getAnchor();
4993             
4994         if(anchor && anchor.fn){
4995             Ext.EventManager.removeResizeListener(anchor.fn);
4996             if(anchor.scroll){
4997                 Ext.EventManager.un(window, 'scroll', anchor.fn);
4998             }
4999             delete anchor.fn;
5000         }
5001         return me;
5002     },
5003     
5004     // private
5005     getAnchor : function(){
5006         var data = Ext.Element.data,
5007             dom = this.dom;
5008             if (!dom) {
5009                 return;
5010             }
5011             var anchor = data(dom, '_anchor');
5012             
5013         if(!anchor){
5014             anchor = data(dom, '_anchor', {});
5015         }
5016         return anchor;
5017     },
5018
5019     /**
5020      * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
5021      * supported position values.
5022      * @param {Mixed} element The element to align to.
5023      * @param {String} position (optional, defaults to "tl-bl?") The position to align to.
5024      * @param {Array} offsets (optional) Offset the positioning by [x, y]
5025      * @return {Array} [x, y]
5026      */
5027     getAlignToXY : function(el, p, o){      
5028         el = Ext.get(el);
5029         
5030         if(!el || !el.dom){
5031             throw "Element.alignToXY with an element that doesn't exist";
5032         }
5033         
5034         o = o || [0,0];
5035         p = (!p || p == "?" ? "tl-bl?" : (!/-/.test(p) && p !== "" ? "tl-" + p : p || "tl-bl")).toLowerCase();       
5036                 
5037         var me = this,
5038                 d = me.dom,
5039                 a1,
5040                 a2,
5041                 x,
5042                 y,
5043                 //constrain the aligned el to viewport if necessary
5044                 w,
5045                 h,
5046                 r,
5047                 dw = Ext.lib.Dom.getViewWidth() -10, // 10px of margin for ie
5048                 dh = Ext.lib.Dom.getViewHeight()-10, // 10px of margin for ie
5049                 p1y,
5050                 p1x,            
5051                 p2y,
5052                 p2x,
5053                 swapY,
5054                 swapX,
5055                 doc = document,
5056                 docElement = doc.documentElement,
5057                 docBody = doc.body,
5058                 scrollX = (docElement.scrollLeft || docBody.scrollLeft || 0)+5,
5059                 scrollY = (docElement.scrollTop || docBody.scrollTop || 0)+5,
5060                 c = false, //constrain to viewport
5061                 p1 = "", 
5062                 p2 = "",
5063                 m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
5064         
5065         if(!m){
5066            throw "Element.alignTo with an invalid alignment " + p;
5067         }
5068         
5069         p1 = m[1]; 
5070         p2 = m[2]; 
5071         c = !!m[3];
5072
5073         //Subtract the aligned el's internal xy from the target's offset xy
5074         //plus custom offset to get the aligned el's new offset xy
5075         a1 = me.getAnchorXY(p1, true);
5076         a2 = el.getAnchorXY(p2, false);
5077
5078         x = a2[0] - a1[0] + o[0];
5079         y = a2[1] - a1[1] + o[1];
5080
5081         if(c){    
5082                w = me.getWidth();
5083            h = me.getHeight();
5084            r = el.getRegion();       
5085            //If we are at a viewport boundary and the aligned el is anchored on a target border that is
5086            //perpendicular to the vp border, allow the aligned el to slide on that border,
5087            //otherwise swap the aligned el to the opposite border of the target.
5088            p1y = p1.charAt(0);
5089            p1x = p1.charAt(p1.length-1);
5090            p2y = p2.charAt(0);
5091            p2x = p2.charAt(p2.length-1);
5092            swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
5093            swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));          
5094            
5095
5096            if (x + w > dw + scrollX) {
5097                 x = swapX ? r.left-w : dw+scrollX-w;
5098            }
5099            if (x < scrollX) {
5100                x = swapX ? r.right : scrollX;
5101            }
5102            if (y + h > dh + scrollY) {
5103                 y = swapY ? r.top-h : dh+scrollY-h;
5104             }
5105            if (y < scrollY){
5106                y = swapY ? r.bottom : scrollY;
5107            }
5108         }
5109         return [x,y];
5110     },
5111
5112     /**
5113      * Aligns this element with another element relative to the specified anchor points. If the other element is the
5114      * document it aligns it to the viewport.
5115      * The position parameter is optional, and can be specified in any one of the following formats:
5116      * <ul>
5117      *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
5118      *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
5119      *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
5120      *       deprecated in favor of the newer two anchor syntax below</i>.</li>
5121      *   <li><b>Two anchors</b>: If two values from the table below are passed separated by a dash, the first value is used as the
5122      *       element's anchor point, and the second value is used as the target's anchor point.</li>
5123      * </ul>
5124      * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
5125      * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
5126      * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
5127      * that specified in order to enforce the viewport constraints.
5128      * Following are all of the supported anchor positions:
5129 <pre>
5130 Value  Description
5131 -----  -----------------------------
5132 tl     The top left corner (default)
5133 t      The center of the top edge
5134 tr     The top right corner
5135 l      The center of the left edge
5136 c      In the center of the element
5137 r      The center of the right edge
5138 bl     The bottom left corner
5139 b      The center of the bottom edge
5140 br     The bottom right corner
5141 </pre>
5142 Example Usage:
5143 <pre><code>
5144 // align el to other-el using the default positioning ("tl-bl", non-constrained)
5145 el.alignTo("other-el");
5146
5147 // align the top left corner of el with the top right corner of other-el (constrained to viewport)
5148 el.alignTo("other-el", "tr?");
5149
5150 // align the bottom right corner of el with the center left edge of other-el
5151 el.alignTo("other-el", "br-l?");
5152
5153 // align the center of el with the bottom left corner of other-el and
5154 // adjust the x position by -6 pixels (and the y position by 0)
5155 el.alignTo("other-el", "c-bl", [-6, 0]);
5156 </code></pre>
5157      * @param {Mixed} element The element to align to.
5158      * @param {String} position (optional, defaults to "tl-bl?") The position to align to.
5159      * @param {Array} offsets (optional) Offset the positioning by [x, y]
5160      * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
5161      * @return {Ext.Element} this
5162      */
5163     alignTo : function(element, position, offsets, animate){
5164             var me = this;
5165         return me.setXY(me.getAlignToXY(element, position, offsets),
5166                                 me.preanim && !!animate ? me.preanim(arguments, 3) : false);
5167     },
5168     
5169     // private ==>  used outside of core
5170     adjustForConstraints : function(xy, parent, offsets){
5171         return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
5172     },
5173
5174     // private ==>  used outside of core
5175     getConstrainToXY : function(el, local, offsets, proposedXY){   
5176             var os = {top:0, left:0, bottom:0, right: 0};
5177
5178         return function(el, local, offsets, proposedXY){
5179             el = Ext.get(el);
5180             offsets = offsets ? Ext.applyIf(offsets, os) : os;
5181
5182             var vw, vh, vx = 0, vy = 0;
5183             if(el.dom == document.body || el.dom == document){
5184                 vw =Ext.lib.Dom.getViewWidth();
5185                 vh = Ext.lib.Dom.getViewHeight();
5186             }else{
5187                 vw = el.dom.clientWidth;
5188                 vh = el.dom.clientHeight;
5189                 if(!local){
5190                     var vxy = el.getXY();
5191                     vx = vxy[0];
5192                     vy = vxy[1];
5193                 }
5194             }
5195
5196             var s = el.getScroll();
5197
5198             vx += offsets.left + s.left;
5199             vy += offsets.top + s.top;
5200
5201             vw -= offsets.right;
5202             vh -= offsets.bottom;
5203
5204             var vr = vx+vw;
5205             var vb = vy+vh;
5206
5207             var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
5208             var x = xy[0], y = xy[1];
5209             var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
5210
5211             // only move it if it needs it
5212             var moved = false;
5213
5214             // first validate right/bottom
5215             if((x + w) > vr){
5216                 x = vr - w;
5217                 moved = true;
5218             }
5219             if((y + h) > vb){
5220                 y = vb - h;
5221                 moved = true;
5222             }
5223             // then make sure top/left isn't negative
5224             if(x < vx){
5225                 x = vx;
5226                 moved = true;
5227             }
5228             if(y < vy){
5229                 y = vy;
5230                 moved = true;
5231             }
5232             return moved ? [x, y] : false;
5233         };
5234     }(),
5235             
5236             
5237                 
5238 //         el = Ext.get(el);
5239 //         offsets = Ext.applyIf(offsets || {}, {top : 0, left : 0, bottom : 0, right : 0});
5240
5241 //         var  me = this,
5242 //              doc = document,
5243 //              s = el.getScroll(),
5244 //              vxy = el.getXY(),
5245 //              vx = offsets.left + s.left, 
5246 //              vy = offsets.top + s.top,               
5247 //              vw = -offsets.right, 
5248 //              vh = -offsets.bottom, 
5249 //              vr,
5250 //              vb,
5251 //              xy = proposedXY || (!local ? me.getXY() : [me.getLeft(true), me.getTop(true)]),
5252 //              x = xy[0],
5253 //              y = xy[1],
5254 //              w = me.dom.offsetWidth, h = me.dom.offsetHeight,
5255 //              moved = false; // only move it if it needs it
5256 //       
5257 //              
5258 //         if(el.dom == doc.body || el.dom == doc){
5259 //             vw += Ext.lib.Dom.getViewWidth();
5260 //             vh += Ext.lib.Dom.getViewHeight();
5261 //         }else{
5262 //             vw += el.dom.clientWidth;
5263 //             vh += el.dom.clientHeight;
5264 //             if(!local){                    
5265 //                 vx += vxy[0];
5266 //                 vy += vxy[1];
5267 //             }
5268 //         }
5269
5270 //         // first validate right/bottom
5271 //         if(x + w > vx + vw){
5272 //             x = vx + vw - w;
5273 //             moved = true;
5274 //         }
5275 //         if(y + h > vy + vh){
5276 //             y = vy + vh - h;
5277 //             moved = true;
5278 //         }
5279 //         // then make sure top/left isn't negative
5280 //         if(x < vx){
5281 //             x = vx;
5282 //             moved = true;
5283 //         }
5284 //         if(y < vy){
5285 //             y = vy;
5286 //             moved = true;
5287 //         }
5288 //         return moved ? [x, y] : false;
5289 //    },
5290     
5291     /**
5292     * Calculates the x, y to center this element on the screen
5293     * @return {Array} The x, y values [x, y]
5294     */
5295     getCenterXY : function(){
5296         return this.getAlignToXY(document, 'c-c');
5297     },
5298
5299     /**
5300     * Centers the Element in either the viewport, or another Element.
5301     * @param {Mixed} centerIn (optional) The element in which to center the element.
5302     */
5303     center : function(centerIn){
5304         return this.alignTo(centerIn || document, 'c-c');        
5305     }    
5306 });
5307 /**\r
5308  * @class Ext.Element\r
5309  */\r
5310 Ext.Element.addMethods(function(){\r
5311         var PARENTNODE = 'parentNode',\r
5312                 NEXTSIBLING = 'nextSibling',\r
5313                 PREVIOUSSIBLING = 'previousSibling',\r
5314                 DQ = Ext.DomQuery,\r
5315                 GET = Ext.get;\r
5316         \r
5317         return {\r
5318                 /**\r
5319              * Looks at this node and then at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)\r
5320              * @param {String} selector The simple selector to test\r
5321              * @param {Number/Mixed} maxDepth (optional) The max depth to search as a number or element (defaults to 50 || document.body)\r
5322              * @param {Boolean} returnEl (optional) True to return a Ext.Element object instead of DOM node\r
5323              * @return {HTMLElement} The matching DOM node (or null if no match was found)\r
5324              */\r
5325             findParent : function(simpleSelector, maxDepth, returnEl){\r
5326                 var p = this.dom,\r
5327                         b = document.body, \r
5328                         depth = 0,                      \r
5329                         stopEl;         \r
5330             if(Ext.isGecko && Object.prototype.toString.call(p) == '[object XULElement]') {\r
5331                 return null;\r
5332             }\r
5333                 maxDepth = maxDepth || 50;\r
5334                 if (isNaN(maxDepth)) {\r
5335                     stopEl = Ext.getDom(maxDepth);\r
5336                     maxDepth = Number.MAX_VALUE;\r
5337                 }\r
5338                 while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){\r
5339                     if(DQ.is(p, simpleSelector)){\r
5340                         return returnEl ? GET(p) : p;\r
5341                     }\r
5342                     depth++;\r
5343                     p = p.parentNode;\r
5344                 }\r
5345                 return null;\r
5346             },\r
5347         \r
5348             /**\r
5349              * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)\r
5350              * @param {String} selector The simple selector to test\r
5351              * @param {Number/Mixed} maxDepth (optional) The max depth to\r
5352                     search as a number or element (defaults to 10 || document.body)\r
5353              * @param {Boolean} returnEl (optional) True to return a Ext.Element object instead of DOM node\r
5354              * @return {HTMLElement} The matching DOM node (or null if no match was found)\r
5355              */\r
5356             findParentNode : function(simpleSelector, maxDepth, returnEl){\r
5357                 var p = Ext.fly(this.dom.parentNode, '_internal');\r
5358                 return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;\r
5359             },\r
5360         \r
5361             /**\r
5362              * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).\r
5363              * This is a shortcut for findParentNode() that always returns an Ext.Element.\r
5364              * @param {String} selector The simple selector to test\r
5365              * @param {Number/Mixed} maxDepth (optional) The max depth to\r
5366                     search as a number or element (defaults to 10 || document.body)\r
5367              * @return {Ext.Element} The matching DOM node (or null if no match was found)\r
5368              */\r
5369             up : function(simpleSelector, maxDepth){\r
5370                 return this.findParentNode(simpleSelector, maxDepth, true);\r
5371             },\r
5372         \r
5373             /**\r
5374              * Creates a {@link Ext.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).\r
5375              * @param {String} selector The CSS selector\r
5376              * @return {CompositeElement/CompositeElementLite} The composite element\r
5377              */\r
5378             select : function(selector){\r
5379                 return Ext.Element.select(selector, this.dom);\r
5380             },\r
5381         \r
5382             /**\r
5383              * Selects child nodes based on the passed CSS selector (the selector should not contain an id).\r
5384              * @param {String} selector The CSS selector\r
5385              * @return {Array} An array of the matched nodes\r
5386              */\r
5387             query : function(selector){\r
5388                 return DQ.select(selector, this.dom);\r
5389             },\r
5390         \r
5391             /**\r
5392              * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).\r
5393              * @param {String} selector The CSS selector\r
5394              * @param {Boolean} returnDom (optional) True to return the DOM node instead of Ext.Element (defaults to false)\r
5395              * @return {HTMLElement/Ext.Element} The child Ext.Element (or DOM node if returnDom = true)\r
5396              */\r
5397             child : function(selector, returnDom){\r
5398                 var n = DQ.selectNode(selector, this.dom);\r
5399                 return returnDom ? n : GET(n);\r
5400             },\r
5401         \r
5402             /**\r
5403              * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).\r
5404              * @param {String} selector The CSS selector\r
5405              * @param {Boolean} returnDom (optional) True to return the DOM node instead of Ext.Element (defaults to false)\r
5406              * @return {HTMLElement/Ext.Element} The child Ext.Element (or DOM node if returnDom = true)\r
5407              */\r
5408             down : function(selector, returnDom){\r
5409                 var n = DQ.selectNode(" > " + selector, this.dom);\r
5410                 return returnDom ? n : GET(n);\r
5411             },\r
5412         \r
5413                  /**\r
5414              * Gets the parent node for this element, optionally chaining up trying to match a selector\r
5415              * @param {String} selector (optional) Find a parent node that matches the passed simple selector\r
5416              * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.Element\r
5417              * @return {Ext.Element/HTMLElement} The parent node or null\r
5418                  */\r
5419             parent : function(selector, returnDom){\r
5420                 return this.matchNode(PARENTNODE, PARENTNODE, selector, returnDom);\r
5421             },\r
5422         \r
5423              /**\r
5424              * Gets the next sibling, skipping text nodes\r
5425              * @param {String} selector (optional) Find the next sibling that matches the passed simple selector\r
5426              * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.Element\r
5427              * @return {Ext.Element/HTMLElement} The next sibling or null\r
5428                  */\r
5429             next : function(selector, returnDom){\r
5430                 return this.matchNode(NEXTSIBLING, NEXTSIBLING, selector, returnDom);\r
5431             },\r
5432         \r
5433             /**\r
5434              * Gets the previous sibling, skipping text nodes\r
5435              * @param {String} selector (optional) Find the previous sibling that matches the passed simple selector\r
5436              * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.Element\r
5437              * @return {Ext.Element/HTMLElement} The previous sibling or null\r
5438                  */\r
5439             prev : function(selector, returnDom){\r
5440                 return this.matchNode(PREVIOUSSIBLING, PREVIOUSSIBLING, selector, returnDom);\r
5441             },\r
5442         \r
5443         \r
5444             /**\r
5445              * Gets the first child, skipping text nodes\r
5446              * @param {String} selector (optional) Find the next sibling that matches the passed simple selector\r
5447              * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.Element\r
5448              * @return {Ext.Element/HTMLElement} The first child or null\r
5449                  */\r
5450             first : function(selector, returnDom){\r
5451                 return this.matchNode(NEXTSIBLING, 'firstChild', selector, returnDom);\r
5452             },\r
5453         \r
5454             /**\r
5455              * Gets the last child, skipping text nodes\r
5456              * @param {String} selector (optional) Find the previous sibling that matches the passed simple selector\r
5457              * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.Element\r
5458              * @return {Ext.Element/HTMLElement} The last child or null\r
5459                  */\r
5460             last : function(selector, returnDom){\r
5461                 return this.matchNode(PREVIOUSSIBLING, 'lastChild', selector, returnDom);\r
5462             },\r
5463             \r
5464             matchNode : function(dir, start, selector, returnDom){\r
5465                 var n = this.dom[start];\r
5466                 while(n){\r
5467                     if(n.nodeType == 1 && (!selector || DQ.is(n, selector))){\r
5468                         return !returnDom ? GET(n) : n;\r
5469                     }\r
5470                     n = n[dir];\r
5471                 }\r
5472                 return null;\r
5473             }   \r
5474     }\r
5475 }());/**\r
5476  * @class Ext.Element\r
5477  */\r
5478 Ext.Element.addMethods({\r
5479     /**\r
5480      * Creates a {@link Ext.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).\r
5481      * @param {String} selector The CSS selector\r
5482      * @param {Boolean} unique (optional) True to create a unique Ext.Element for each child (defaults to false, which creates a single shared flyweight object)\r
5483      * @return {CompositeElement/CompositeElementLite} The composite element\r
5484      */\r
5485     select : function(selector, unique){\r
5486         return Ext.Element.select(selector, unique, this.dom);\r
5487     }\r
5488 });/**\r
5489  * @class Ext.Element\r
5490  */\r
5491 Ext.Element.addMethods(\r
5492 function() {\r
5493         var GETDOM = Ext.getDom,\r
5494                 GET = Ext.get,\r
5495                 DH = Ext.DomHelper;\r
5496         \r
5497         return {\r
5498             /**\r
5499              * Appends the passed element(s) to this element\r
5500              * @param {String/HTMLElement/Array/Element/CompositeElement} el\r
5501              * @return {Ext.Element} this\r
5502              */\r
5503             appendChild: function(el){        \r
5504                 return GET(el).appendTo(this);        \r
5505             },\r
5506         \r
5507             /**\r
5508              * Appends this element to the passed element\r
5509              * @param {Mixed} el The new parent element\r
5510              * @return {Ext.Element} this\r
5511              */\r
5512             appendTo: function(el){        \r
5513                 GETDOM(el).appendChild(this.dom);        \r
5514                 return this;\r
5515             },\r
5516         \r
5517             /**\r
5518              * Inserts this element before the passed element in the DOM\r
5519              * @param {Mixed} el The element before which this element will be inserted\r
5520              * @return {Ext.Element} this\r
5521              */\r
5522             insertBefore: function(el){                   \r
5523                 (el = GETDOM(el)).parentNode.insertBefore(this.dom, el);\r
5524                 return this;\r
5525             },\r
5526         \r
5527             /**\r
5528              * Inserts this element after the passed element in the DOM\r
5529              * @param {Mixed} el The element to insert after\r
5530              * @return {Ext.Element} this\r
5531              */\r
5532             insertAfter: function(el){\r
5533                 (el = GETDOM(el)).parentNode.insertBefore(this.dom, el.nextSibling);\r
5534                 return this;\r
5535             },\r
5536         \r
5537             /**\r
5538              * Inserts (or creates) an element (or DomHelper config) as the first child of this element\r
5539              * @param {Mixed/Object} el The id or element to insert or a DomHelper config to create and insert\r
5540              * @return {Ext.Element} The new child\r
5541              */\r
5542             insertFirst: function(el, returnDom){\r
5543             el = el || {};\r
5544             if(el.nodeType || el.dom || typeof el == 'string'){ // element\r
5545                 el = GETDOM(el);\r
5546                 this.dom.insertBefore(el, this.dom.firstChild);\r
5547                 return !returnDom ? GET(el) : el;\r
5548             }else{ // dh config\r
5549                 return this.createChild(el, this.dom.firstChild, returnDom);\r
5550             }\r
5551         },\r
5552         \r
5553             /**\r
5554              * Replaces the passed element with this element\r
5555              * @param {Mixed} el The element to replace\r
5556              * @return {Ext.Element} this\r
5557              */\r
5558             replace: function(el){\r
5559                 el = GET(el);\r
5560                 this.insertBefore(el);\r
5561                 el.remove();\r
5562                 return this;\r
5563             },\r
5564         \r
5565             /**\r
5566              * Replaces this element with the passed element\r
5567              * @param {Mixed/Object} el The new element or a DomHelper config of an element to create\r
5568              * @return {Ext.Element} this\r
5569              */\r
5570             replaceWith: function(el){\r
5571                     var me = this;\r
5572                 \r
5573             if(el.nodeType || el.dom || typeof el == 'string'){\r
5574                 el = GETDOM(el);\r
5575                 me.dom.parentNode.insertBefore(el, me.dom);\r
5576             }else{\r
5577                 el = DH.insertBefore(me.dom, el);\r
5578             }\r
5579                 \r
5580                 delete Ext.elCache[me.id];\r
5581                 Ext.removeNode(me.dom);      \r
5582                 me.id = Ext.id(me.dom = el);\r
5583                 Ext.Element.addToCache(me.isFlyweight ? new Ext.Element(me.dom) : me);     \r
5584             return me;\r
5585             },\r
5586             \r
5587                 /**\r
5588                  * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.\r
5589                  * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be\r
5590                  * automatically generated with the specified attributes.\r
5591                  * @param {HTMLElement} insertBefore (optional) a child element of this element\r
5592                  * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element\r
5593                  * @return {Ext.Element} The new child element\r
5594                  */\r
5595                 createChild: function(config, insertBefore, returnDom){\r
5596                     config = config || {tag:'div'};\r
5597                     return insertBefore ? \r
5598                            DH.insertBefore(insertBefore, config, returnDom !== true) :  \r
5599                            DH[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);\r
5600                 },\r
5601                 \r
5602                 /**\r
5603                  * Creates and wraps this element with another element\r
5604                  * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div\r
5605                  * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Ext.Element\r
5606                  * @return {HTMLElement/Element} The newly created wrapper element\r
5607                  */\r
5608                 wrap: function(config, returnDom){        \r
5609                     var newEl = DH.insertBefore(this.dom, config || {tag: "div"}, !returnDom);\r
5610                     newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);\r
5611                     return newEl;\r
5612                 },\r
5613                 \r
5614                 /**\r
5615                  * Inserts an html fragment into this element\r
5616                  * @param {String} where Where to insert the html in relation to this element - beforeBegin, afterBegin, beforeEnd, afterEnd.\r
5617                  * @param {String} html The HTML fragment\r
5618                  * @param {Boolean} returnEl (optional) True to return an Ext.Element (defaults to false)\r
5619                  * @return {HTMLElement/Ext.Element} The inserted node (or nearest related if more than 1 inserted)\r
5620                  */\r
5621                 insertHtml : function(where, html, returnEl){\r
5622                     var el = DH.insertHtml(where, this.dom, html);\r
5623                     return returnEl ? Ext.get(el) : el;\r
5624                 }\r
5625         }\r
5626 }());/**\r
5627  * @class Ext.Element\r
5628  */\r
5629 Ext.apply(Ext.Element.prototype, function() {\r
5630         var GETDOM = Ext.getDom,\r
5631                 GET = Ext.get,\r
5632                 DH = Ext.DomHelper;\r
5633         \r
5634         return {        \r
5635                 /**\r
5636              * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element\r
5637              * @param {Mixed/Object/Array} el The id, element to insert or a DomHelper config to create and insert *or* an array of any of those.\r
5638              * @param {String} where (optional) 'before' or 'after' defaults to before\r
5639              * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Ext.Element\r
5640              * @return {Ext.Element} The inserted Element. If an array is passed, the last inserted element is returned.\r
5641              */\r
5642             insertSibling: function(el, where, returnDom){\r
5643                 var me = this,\r
5644                         rt,\r
5645                 isAfter = (where || 'before').toLowerCase() == 'after',\r
5646                 insertEl;\r
5647                         \r
5648                 if(Ext.isArray(el)){\r
5649                 insertEl = me;\r
5650                     Ext.each(el, function(e) {\r
5651                             rt = Ext.fly(insertEl, '_internal').insertSibling(e, where, returnDom);\r
5652                     if(isAfter){\r
5653                         insertEl = rt;\r
5654                     }\r
5655                     });\r
5656                     return rt;\r
5657                 }\r
5658                         \r
5659                 el = el || {};\r
5660                 \r
5661             if(el.nodeType || el.dom){\r
5662                 rt = me.dom.parentNode.insertBefore(GETDOM(el), isAfter ? me.dom.nextSibling : me.dom);\r
5663                 if (!returnDom) {\r
5664                     rt = GET(rt);\r
5665                 }\r
5666             }else{\r
5667                 if (isAfter && !me.dom.nextSibling) {\r
5668                     rt = DH.append(me.dom.parentNode, el, !returnDom);\r
5669                 } else {                    \r
5670                     rt = DH[isAfter ? 'insertAfter' : 'insertBefore'](me.dom, el, !returnDom);\r
5671                 }\r
5672             }\r
5673                 return rt;\r
5674             }\r
5675     };\r
5676 }());/**
5677  * @class Ext.Element
5678  */
5679 Ext.Element.addMethods(function(){
5680     // local style camelizing for speed
5681     var propCache = {},
5682         camelRe = /(-[a-z])/gi,
5683         classReCache = {},
5684         view = document.defaultView,
5685         propFloat = Ext.isIE ? 'styleFloat' : 'cssFloat',
5686         opacityRe = /alpha\(opacity=(.*)\)/i,
5687         trimRe = /^\s+|\s+$/g,
5688         EL = Ext.Element,
5689         PADDING = "padding",
5690         MARGIN = "margin",
5691         BORDER = "border",
5692         LEFT = "-left",
5693         RIGHT = "-right",
5694         TOP = "-top",
5695         BOTTOM = "-bottom",
5696         WIDTH = "-width",
5697         MATH = Math,
5698         HIDDEN = 'hidden',
5699         ISCLIPPED = 'isClipped',
5700         OVERFLOW = 'overflow',
5701         OVERFLOWX = 'overflow-x',
5702         OVERFLOWY = 'overflow-y',
5703         ORIGINALCLIP = 'originalClip',
5704         // special markup used throughout Ext when box wrapping elements
5705         borders = {l: BORDER + LEFT + WIDTH, r: BORDER + RIGHT + WIDTH, t: BORDER + TOP + WIDTH, b: BORDER + BOTTOM + WIDTH},
5706         paddings = {l: PADDING + LEFT, r: PADDING + RIGHT, t: PADDING + TOP, b: PADDING + BOTTOM},
5707         margins = {l: MARGIN + LEFT, r: MARGIN + RIGHT, t: MARGIN + TOP, b: MARGIN + BOTTOM},
5708         data = Ext.Element.data;
5709
5710
5711     // private
5712     function camelFn(m, a) {
5713         return a.charAt(1).toUpperCase();
5714     }
5715
5716     function chkCache(prop) {
5717         return propCache[prop] || (propCache[prop] = prop == 'float' ? propFloat : prop.replace(camelRe, camelFn));
5718     }
5719
5720     return {
5721         // private  ==> used by Fx
5722         adjustWidth : function(width) {
5723             var me = this;
5724             var isNum = Ext.isNumber(width);
5725             if(isNum && me.autoBoxAdjust && !me.isBorderBox()){
5726                width -= (me.getBorderWidth("lr") + me.getPadding("lr"));
5727             }
5728             return (isNum && width < 0) ? 0 : width;
5729         },
5730
5731         // private   ==> used by Fx
5732         adjustHeight : function(height) {
5733             var me = this;
5734             var isNum = Ext.isNumber(height);
5735             if(isNum && me.autoBoxAdjust && !me.isBorderBox()){
5736                height -= (me.getBorderWidth("tb") + me.getPadding("tb"));
5737             }
5738             return (isNum && height < 0) ? 0 : height;
5739         },
5740
5741
5742         /**
5743          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
5744          * @param {String/Array} className The CSS class to add, or an array of classes
5745          * @return {Ext.Element} this
5746          */
5747         addClass : function(className){
5748             var me = this, i, len, v;
5749             className = Ext.isArray(className) ? className : [className];
5750             for (i=0, len = className.length; i < len; i++) {
5751                 v = className[i];
5752                 if (v) {
5753                     me.dom.className += (!me.hasClass(v) && v ? " " + v : "");
5754                 };
5755             };
5756             return me;
5757         },
5758
5759         /**
5760          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
5761          * @param {String/Array} className The CSS class to add, or an array of classes
5762          * @return {Ext.Element} this
5763          */
5764         radioClass : function(className){
5765             var cn = this.dom.parentNode.childNodes, v;
5766             className = Ext.isArray(className) ? className : [className];
5767             for (var i=0, len = cn.length; i < len; i++) {
5768                 v = cn[i];
5769                 if(v && v.nodeType == 1) {
5770                     Ext.fly(v, '_internal').removeClass(className);
5771                 }
5772             };
5773             return this.addClass(className);
5774         },
5775
5776         /**
5777          * Removes one or more CSS classes from the element.
5778          * @param {String/Array} className The CSS class to remove, or an array of classes
5779          * @return {Ext.Element} this
5780          */
5781         removeClass : function(className){
5782             var me = this, v;
5783             className = Ext.isArray(className) ? className : [className];
5784             if (me.dom && me.dom.className) {
5785                 for (var i=0, len=className.length; i < len; i++) {
5786                     v = className[i];
5787                     if(v) {
5788                         me.dom.className = me.dom.className.replace(
5789                             classReCache[v] = classReCache[v] || new RegExp('(?:^|\\s+)' + v + '(?:\\s+|$)', "g"), " "
5790                         );
5791                     }
5792                 };
5793             }
5794             return me;
5795         },
5796
5797         /**
5798          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
5799          * @param {String} className The CSS class to toggle
5800          * @return {Ext.Element} this
5801          */
5802         toggleClass : function(className){
5803             return this.hasClass(className) ? this.removeClass(className) : this.addClass(className);
5804         },
5805
5806         /**
5807          * Checks if the specified CSS class exists on this element's DOM node.
5808          * @param {String} className The CSS class to check for
5809          * @return {Boolean} True if the class exists, else false
5810          */
5811         hasClass : function(className){
5812             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
5813         },
5814
5815         /**
5816          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
5817          * @param {String} oldClassName The CSS class to replace
5818          * @param {String} newClassName The replacement CSS class
5819          * @return {Ext.Element} this
5820          */
5821         replaceClass : function(oldClassName, newClassName){
5822             return this.removeClass(oldClassName).addClass(newClassName);
5823         },
5824
5825         isStyle : function(style, val) {
5826             return this.getStyle(style) == val;
5827         },
5828
5829         /**
5830          * Normalizes currentStyle and computedStyle.
5831          * @param {String} property The style property whose value is returned.
5832          * @return {String} The current value of the style property for this element.
5833          */
5834         getStyle : function(){
5835             return view && view.getComputedStyle ?
5836                 function(prop){
5837                     var el = this.dom,
5838                         v,
5839                         cs,
5840                         out,
5841                         display,
5842                         wk = Ext.isWebKit,
5843                         display;
5844                         
5845                     if(el == document){
5846                         return null;
5847                     }
5848                     prop = chkCache(prop);
5849                     // Fix bug caused by this: https://bugs.webkit.org/show_bug.cgi?id=13343
5850                     if(wk && /marginRight/.test(prop)){
5851                         display = this.getStyle('display');
5852                         el.style.display = 'inline-block';
5853                     }
5854                     out = (v = el.style[prop]) ? v :
5855                            (cs = view.getComputedStyle(el, "")) ? cs[prop] : null;
5856
5857                     // Webkit returns rgb values for transparent.
5858                     if(wk){
5859                         if(out == 'rgba(0, 0, 0, 0)'){
5860                             out = 'transparent';
5861                         }else if(display){
5862                             el.style.display = display;
5863                         }
5864                     }
5865                     return out;
5866                 } :
5867                 function(prop){
5868                     var el = this.dom,
5869                         m,
5870                         cs;
5871
5872                     if(el == document) return null;
5873                     if (prop == 'opacity') {
5874                         if (el.style.filter.match) {
5875                             if(m = el.style.filter.match(opacityRe)){
5876                                 var fv = parseFloat(m[1]);
5877                                 if(!isNaN(fv)){
5878                                     return fv ? fv / 100 : 0;
5879                                 }
5880                             }
5881                         }
5882                         return 1;
5883                     }
5884                     prop = chkCache(prop);
5885                     return el.style[prop] || ((cs = el.currentStyle) ? cs[prop] : null);
5886                 };
5887         }(),
5888
5889         /**
5890          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
5891          * are convert to standard 6 digit hex color.
5892          * @param {String} attr The css attribute
5893          * @param {String} defaultValue The default value to use when a valid color isn't found
5894          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
5895          * color anims.
5896          */
5897         getColor : function(attr, defaultValue, prefix){
5898             var v = this.getStyle(attr),
5899                 color = Ext.isDefined(prefix) ? prefix : '#',
5900                 h;
5901
5902             if(!v || /transparent|inherit/.test(v)){
5903                 return defaultValue;
5904             }
5905             if(/^r/.test(v)){
5906                 Ext.each(v.slice(4, v.length -1).split(','), function(s){
5907                     h = parseInt(s, 10);
5908                     color += (h < 16 ? '0' : '') + h.toString(16);
5909                 });
5910             }else{
5911                 v = v.replace('#', '');
5912                 color += v.length == 3 ? v.replace(/^(\w)(\w)(\w)$/, '$1$1$2$2$3$3') : v;
5913             }
5914             return(color.length > 5 ? color.toLowerCase() : defaultValue);
5915         },
5916
5917         /**
5918          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
5919          * @param {String/Object} property The style property to be set, or an object of multiple styles.
5920          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
5921          * @return {Ext.Element} this
5922          */
5923         setStyle : function(prop, value){
5924             var tmp,
5925                 style,
5926                 camel;
5927             if (!Ext.isObject(prop)) {
5928                 tmp = {};
5929                 tmp[prop] = value;
5930                 prop = tmp;
5931             }
5932             for (style in prop) {
5933                 value = prop[style];
5934                 style == 'opacity' ?
5935                     this.setOpacity(value) :
5936                     this.dom.style[chkCache(style)] = value;
5937             }
5938             return this;
5939         },
5940
5941         /**
5942          * Set the opacity of the element
5943          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
5944          * @param {Boolean/Object} animate (optional) a standard Element animation config object or <tt>true</tt> for
5945          * the default animation (<tt>{duration: .35, easing: 'easeIn'}</tt>)
5946          * @return {Ext.Element} this
5947          */
5948          setOpacity : function(opacity, animate){
5949             var me = this,
5950                 s = me.dom.style;
5951
5952             if(!animate || !me.anim){
5953                 if(Ext.isIE){
5954                     var opac = opacity < 1 ? 'alpha(opacity=' + opacity * 100 + ')' : '',
5955                     val = s.filter.replace(opacityRe, '').replace(trimRe, '');
5956
5957                     s.zoom = 1;
5958                     s.filter = val + (val.length > 0 ? ' ' : '') + opac;
5959                 }else{
5960                     s.opacity = opacity;
5961                 }
5962             }else{
5963                 me.anim({opacity: {to: opacity}}, me.preanim(arguments, 1), null, .35, 'easeIn');
5964             }
5965             return me;
5966         },
5967
5968         /**
5969          * Clears any opacity settings from this element. Required in some cases for IE.
5970          * @return {Ext.Element} this
5971          */
5972         clearOpacity : function(){
5973             var style = this.dom.style;
5974             if(Ext.isIE){
5975                 if(!Ext.isEmpty(style.filter)){
5976                     style.filter = style.filter.replace(opacityRe, '').replace(trimRe, '');
5977                 }
5978             }else{
5979                 style.opacity = style['-moz-opacity'] = style['-khtml-opacity'] = '';
5980             }
5981             return this;
5982         },
5983
5984         /**
5985          * Returns the offset height of the element
5986          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
5987          * @return {Number} The element's height
5988          */
5989         getHeight : function(contentHeight){
5990             var me = this,
5991                 dom = me.dom,
5992                 hidden = Ext.isIE && me.isStyle('display', 'none'),
5993                 h = MATH.max(dom.offsetHeight, hidden ? 0 : dom.clientHeight) || 0;
5994
5995             h = !contentHeight ? h : h - me.getBorderWidth("tb") - me.getPadding("tb");
5996             return h < 0 ? 0 : h;
5997         },
5998
5999         /**
6000          * Returns the offset width of the element
6001          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
6002          * @return {Number} The element's width
6003          */
6004         getWidth : function(contentWidth){
6005             var me = this,
6006                 dom = me.dom,
6007                 hidden = Ext.isIE && me.isStyle('display', 'none'),
6008                 w = MATH.max(dom.offsetWidth, hidden ? 0 : dom.clientWidth) || 0;
6009             w = !contentWidth ? w : w - me.getBorderWidth("lr") - me.getPadding("lr");
6010             return w < 0 ? 0 : w;
6011         },
6012
6013         /**
6014          * Set the width of this Element.
6015          * @param {Mixed} width The new width. This may be one of:<div class="mdetail-params"><ul>
6016          * <li>A Number specifying the new width in this Element's {@link #defaultUnit}s (by default, pixels).</li>
6017          * <li>A String used to set the CSS width style. Animation may <b>not</b> be used.
6018          * </ul></div>
6019          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
6020          * @return {Ext.Element} this
6021          */
6022         setWidth : function(width, animate){
6023             var me = this;
6024             width = me.adjustWidth(width);
6025             !animate || !me.anim ?
6026                 me.dom.style.width = me.addUnits(width) :
6027                 me.anim({width : {to : width}}, me.preanim(arguments, 1));
6028             return me;
6029         },
6030
6031         /**
6032          * Set the height of this Element.
6033          * <pre><code>
6034 // change the height to 200px and animate with default configuration
6035 Ext.fly('elementId').setHeight(200, true);
6036
6037 // change the height to 150px and animate with a custom configuration
6038 Ext.fly('elId').setHeight(150, {
6039     duration : .5, // animation will have a duration of .5 seconds
6040     // will change the content to "finished"
6041     callback: function(){ this.{@link #update}("finished"); }
6042 });
6043          * </code></pre>
6044          * @param {Mixed} height The new height. This may be one of:<div class="mdetail-params"><ul>
6045          * <li>A Number specifying the new height in this Element's {@link #defaultUnit}s (by default, pixels.)</li>
6046          * <li>A String used to set the CSS height style. Animation may <b>not</b> be used.</li>
6047          * </ul></div>
6048          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
6049          * @return {Ext.Element} this
6050          */
6051          setHeight : function(height, animate){
6052             var me = this;
6053             height = me.adjustHeight(height);
6054             !animate || !me.anim ?
6055                 me.dom.style.height = me.addUnits(height) :
6056                 me.anim({height : {to : height}}, me.preanim(arguments, 1));
6057             return me;
6058         },
6059
6060         /**
6061          * Gets the width of the border(s) for the specified side(s)
6062          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
6063          * passing <tt>'lr'</tt> would get the border <b><u>l</u></b>eft width + the border <b><u>r</u></b>ight width.
6064          * @return {Number} The width of the sides passed added together
6065          */
6066         getBorderWidth : function(side){
6067             return this.addStyles(side, borders);
6068         },
6069
6070         /**
6071          * Gets the width of the padding(s) for the specified side(s)
6072          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
6073          * passing <tt>'lr'</tt> would get the padding <b><u>l</u></b>eft + the padding <b><u>r</u></b>ight.
6074          * @return {Number} The padding of the sides passed added together
6075          */
6076         getPadding : function(side){
6077             return this.addStyles(side, paddings);
6078         },
6079
6080         /**
6081          *  Store the current overflow setting and clip overflow on the element - use <tt>{@link #unclip}</tt> to remove
6082          * @return {Ext.Element} this
6083          */
6084         clip : function(){
6085             var me = this,
6086                 dom = me.dom;
6087
6088             if(!data(dom, ISCLIPPED)){
6089                 data(dom, ISCLIPPED, true);
6090                 data(dom, ORIGINALCLIP, {
6091                     o: me.getStyle(OVERFLOW),
6092                     x: me.getStyle(OVERFLOWX),
6093                     y: me.getStyle(OVERFLOWY)
6094                 });
6095                 me.setStyle(OVERFLOW, HIDDEN);
6096                 me.setStyle(OVERFLOWX, HIDDEN);
6097                 me.setStyle(OVERFLOWY, HIDDEN);
6098             }
6099             return me;
6100         },
6101
6102         /**
6103          *  Return clipping (overflow) to original clipping before <tt>{@link #clip}</tt> was called
6104          * @return {Ext.Element} this
6105          */
6106         unclip : function(){
6107             var me = this,
6108                 dom = me.dom;
6109
6110             if(data(dom, ISCLIPPED)){
6111                 data(dom, ISCLIPPED, false);
6112                 var o = data(dom, ORIGINALCLIP);
6113                 if(o.o){
6114                     me.setStyle(OVERFLOW, o.o);
6115                 }
6116                 if(o.x){
6117                     me.setStyle(OVERFLOWX, o.x);
6118                 }
6119                 if(o.y){
6120                     me.setStyle(OVERFLOWY, o.y);
6121                 }
6122             }
6123             return me;
6124         },
6125
6126         // private
6127         addStyles : function(sides, styles){
6128             var val = 0,
6129                 m = sides.match(/\w/g),
6130                 s;
6131             for (var i=0, len=m.length; i<len; i++) {
6132                 s = m[i] && parseInt(this.getStyle(styles[m[i]]), 10);
6133                 if (s) {
6134                     val += MATH.abs(s);
6135                 }
6136             }
6137             return val;
6138         },
6139
6140         margins : margins
6141     }
6142 }()
6143 );
6144 /**\r
6145  * @class Ext.Element\r
6146  */\r
6147 \r
6148 // special markup used throughout Ext when box wrapping elements\r
6149 Ext.Element.boxMarkup = '<div class="{0}-tl"><div class="{0}-tr"><div class="{0}-tc"></div></div></div><div class="{0}-ml"><div class="{0}-mr"><div class="{0}-mc"></div></div></div><div class="{0}-bl"><div class="{0}-br"><div class="{0}-bc"></div></div></div>';\r
6150 \r
6151 Ext.Element.addMethods(function(){\r
6152     var INTERNAL = "_internal",\r
6153         pxMatch = /(\d+\.?\d+)px/;\r
6154     return {\r
6155         /**\r
6156          * More flexible version of {@link #setStyle} for setting style properties.\r
6157          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or\r
6158          * a function which returns such a specification.\r
6159          * @return {Ext.Element} this\r
6160          */\r
6161         applyStyles : function(style){\r
6162             Ext.DomHelper.applyStyles(this.dom, style);\r
6163             return this;\r
6164         },\r
6165 \r
6166         /**\r
6167          * Returns an object with properties matching the styles requested.\r
6168          * For example, el.getStyles('color', 'font-size', 'width') might return\r
6169          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.\r
6170          * @param {String} style1 A style name\r
6171          * @param {String} style2 A style name\r
6172          * @param {String} etc.\r
6173          * @return {Object} The style object\r
6174          */\r
6175         getStyles : function(){\r
6176             var ret = {};\r
6177             Ext.each(arguments, function(v) {\r
6178                ret[v] = this.getStyle(v);\r
6179             },\r
6180             this);\r
6181             return ret;\r
6182         },\r
6183 \r
6184         // private  ==> used by ext full\r
6185         setOverflow : function(v){\r
6186             var dom = this.dom;\r
6187             if(v=='auto' && Ext.isMac && Ext.isGecko2){ // work around stupid FF 2.0/Mac scroll bar bug\r
6188                 dom.style.overflow = 'hidden';\r
6189                 (function(){dom.style.overflow = 'auto';}).defer(1);\r
6190             }else{\r
6191                 dom.style.overflow = v;\r
6192             }\r
6193         },\r
6194 \r
6195        /**\r
6196         * <p>Wraps the specified element with a special 9 element markup/CSS block that renders by default as\r
6197         * a gray container with a gradient background, rounded corners and a 4-way shadow.</p>\r
6198         * <p>This special markup is used throughout Ext when box wrapping elements ({@link Ext.Button},\r
6199         * {@link Ext.Panel} when <tt>{@link Ext.Panel#frame frame=true}</tt>, {@link Ext.Window}).  The markup\r
6200         * is of this form:</p>\r
6201         * <pre><code>\r
6202     Ext.Element.boxMarkup =\r
6203     &#39;&lt;div class="{0}-tl">&lt;div class="{0}-tr">&lt;div class="{0}-tc">&lt;/div>&lt;/div>&lt;/div>\r
6204      &lt;div class="{0}-ml">&lt;div class="{0}-mr">&lt;div class="{0}-mc">&lt;/div>&lt;/div>&lt;/div>\r
6205      &lt;div class="{0}-bl">&lt;div class="{0}-br">&lt;div class="{0}-bc">&lt;/div>&lt;/div>&lt;/div>&#39;;\r
6206         * </code></pre>\r
6207         * <p>Example usage:</p>\r
6208         * <pre><code>\r
6209     // Basic box wrap\r
6210     Ext.get("foo").boxWrap();\r
6211 \r
6212     // You can also add a custom class and use CSS inheritance rules to customize the box look.\r
6213     // 'x-box-blue' is a built-in alternative -- look at the related CSS definitions as an example\r
6214     // for how to create a custom box wrap style.\r
6215     Ext.get("foo").boxWrap().addClass("x-box-blue");\r
6216         * </code></pre>\r
6217         * @param {String} class (optional) A base CSS class to apply to the containing wrapper element\r
6218         * (defaults to <tt>'x-box'</tt>). Note that there are a number of CSS rules that are dependent on\r
6219         * this name to make the overall effect work, so if you supply an alternate base class, make sure you\r
6220         * also supply all of the necessary rules.\r
6221         * @return {Ext.Element} The outermost wrapping element of the created box structure.\r
6222         */\r
6223         boxWrap : function(cls){\r
6224             cls = cls || 'x-box';\r
6225             var el = Ext.get(this.insertHtml("beforeBegin", "<div class='" + cls + "'>" + String.format(Ext.Element.boxMarkup, cls) + "</div>"));        //String.format('<div class="{0}">'+Ext.Element.boxMarkup+'</div>', cls)));\r
6226             Ext.DomQuery.selectNode('.' + cls + '-mc', el.dom).appendChild(this.dom);\r
6227             return el;\r
6228         },\r
6229 \r
6230         /**\r
6231          * Set the size of this Element. If animation is true, both width and height will be animated concurrently.\r
6232          * @param {Mixed} width The new width. This may be one of:<div class="mdetail-params"><ul>\r
6233          * <li>A Number specifying the new width in this Element's {@link #defaultUnit}s (by default, pixels).</li>\r
6234          * <li>A String used to set the CSS width style. Animation may <b>not</b> be used.\r
6235          * <li>A size object in the format <code>{width: widthValue, height: heightValue}</code>.</li>\r
6236          * </ul></div>\r
6237          * @param {Mixed} height The new height. This may be one of:<div class="mdetail-params"><ul>\r
6238          * <li>A Number specifying the new height in this Element's {@link #defaultUnit}s (by default, pixels).</li>\r
6239          * <li>A String used to set the CSS height style. Animation may <b>not</b> be used.</li>\r
6240          * </ul></div>\r
6241          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object\r
6242          * @return {Ext.Element} this\r
6243          */\r
6244         setSize : function(width, height, animate){\r
6245             var me = this;\r
6246             if(Ext.isObject(width)){ // in case of object from getSize()\r
6247                 height = width.height;\r
6248                 width = width.width;\r
6249             }\r
6250             width = me.adjustWidth(width);\r
6251             height = me.adjustHeight(height);\r
6252             if(!animate || !me.anim){\r
6253                 me.dom.style.width = me.addUnits(width);\r
6254                 me.dom.style.height = me.addUnits(height);\r
6255             }else{\r
6256                 me.anim({width: {to: width}, height: {to: height}}, me.preanim(arguments, 2));\r
6257             }\r
6258             return me;\r
6259         },\r
6260 \r
6261         /**\r
6262          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders\r
6263          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements\r
6264          * if a height has not been set using CSS.\r
6265          * @return {Number}\r
6266          */\r
6267         getComputedHeight : function(){\r
6268             var me = this,\r
6269                 h = Math.max(me.dom.offsetHeight, me.dom.clientHeight);\r
6270             if(!h){\r
6271                 h = parseFloat(me.getStyle('height')) || 0;\r
6272                 if(!me.isBorderBox()){\r
6273                     h += me.getFrameWidth('tb');\r
6274                 }\r
6275             }\r
6276             return h;\r
6277         },\r
6278 \r
6279         /**\r
6280          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders\r
6281          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements\r
6282          * if a width has not been set using CSS.\r
6283          * @return {Number}\r
6284          */\r
6285         getComputedWidth : function(){\r
6286             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);\r
6287             if(!w){\r
6288                 w = parseFloat(this.getStyle('width')) || 0;\r
6289                 if(!this.isBorderBox()){\r
6290                     w += this.getFrameWidth('lr');\r
6291                 }\r
6292             }\r
6293             return w;\r
6294         },\r
6295 \r
6296         /**\r
6297          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()\r
6298          for more information about the sides.\r
6299          * @param {String} sides\r
6300          * @return {Number}\r
6301          */\r
6302         getFrameWidth : function(sides, onlyContentBox){\r
6303             return onlyContentBox && this.isBorderBox() ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));\r
6304         },\r
6305 \r
6306         /**\r
6307          * Sets up event handlers to add and remove a css class when the mouse is over this element\r
6308          * @param {String} className\r
6309          * @return {Ext.Element} this\r
6310          */\r
6311         addClassOnOver : function(className){\r
6312             this.hover(\r
6313                 function(){\r
6314                     Ext.fly(this, INTERNAL).addClass(className);\r
6315                 },\r
6316                 function(){\r
6317                     Ext.fly(this, INTERNAL).removeClass(className);\r
6318                 }\r
6319             );\r
6320             return this;\r
6321         },\r
6322 \r
6323         /**\r
6324          * Sets up event handlers to add and remove a css class when this element has the focus\r
6325          * @param {String} className\r
6326          * @return {Ext.Element} this\r
6327          */\r
6328         addClassOnFocus : function(className){\r
6329             this.on("focus", function(){\r
6330                 Ext.fly(this, INTERNAL).addClass(className);\r
6331             }, this.dom);\r
6332             this.on("blur", function(){\r
6333                 Ext.fly(this, INTERNAL).removeClass(className);\r
6334             }, this.dom);\r
6335             return this;\r
6336         },\r
6337 \r
6338         /**\r
6339          * Sets up event handlers to add and remove a css class when the mouse is down and then up on this element (a click effect)\r
6340          * @param {String} className\r
6341          * @return {Ext.Element} this\r
6342          */\r
6343         addClassOnClick : function(className){\r
6344             var dom = this.dom;\r
6345             this.on("mousedown", function(){\r
6346                 Ext.fly(dom, INTERNAL).addClass(className);\r
6347                 var d = Ext.getDoc(),\r
6348                     fn = function(){\r
6349                         Ext.fly(dom, INTERNAL).removeClass(className);\r
6350                         d.removeListener("mouseup", fn);\r
6351                     };\r
6352                 d.on("mouseup", fn);\r
6353             });\r
6354             return this;\r
6355         },\r
6356 \r
6357         /**\r
6358          * <p>Returns the dimensions of the element available to lay content out in.<p>\r
6359          * <p>If the element (or any ancestor element) has CSS style <code>display : none</code>, the dimensions will be zero.</p>\r
6360          * example:<pre><code>\r
6361         var vpSize = Ext.getBody().getViewSize();\r
6362 \r
6363         // all Windows created afterwards will have a default value of 90% height and 95% width\r
6364         Ext.Window.override({\r
6365             width: vpSize.width * 0.9,\r
6366             height: vpSize.height * 0.95\r
6367         });\r
6368         // To handle window resizing you would have to hook onto onWindowResize.\r
6369         * </code></pre>\r
6370         *\r
6371         * getViewSize utilizes clientHeight/clientWidth which excludes sizing of scrollbars.\r
6372         * To obtain the size including scrollbars, use getStyleSize\r
6373         *\r
6374         * Sizing of the document body is handled at the adapter level which handles special cases for IE and strict modes, etc.\r
6375         */\r
6376 \r
6377         getViewSize : function(){\r
6378             var doc = document,\r
6379                 d = this.dom,\r
6380                 isDoc = (d == doc || d == doc.body);\r
6381 \r
6382             // If the body, use Ext.lib.Dom\r
6383             if (isDoc) {\r
6384                 var extdom = Ext.lib.Dom;\r
6385                 return {\r
6386                     width : extdom.getViewWidth(),\r
6387                     height : extdom.getViewHeight()\r
6388                 }\r
6389 \r
6390             // Else use clientHeight/clientWidth\r
6391             } else {\r
6392                 return {\r
6393                     width : d.clientWidth,\r
6394                     height : d.clientHeight\r
6395                 }\r
6396             }\r
6397         },\r
6398 \r
6399         /**\r
6400         * <p>Returns the dimensions of the element available to lay content out in.<p>\r
6401         *\r
6402         * getStyleSize utilizes prefers style sizing if present, otherwise it chooses the larger of offsetHeight/clientHeight and offsetWidth/clientWidth.\r
6403         * To obtain the size excluding scrollbars, use getViewSize\r
6404         *\r
6405         * Sizing of the document body is handled at the adapter level which handles special cases for IE and strict modes, etc.\r
6406         */\r
6407 \r
6408         getStyleSize : function(){\r
6409             var me = this,\r
6410                 w, h,\r
6411                 doc = document,\r
6412                 d = this.dom,\r
6413                 isDoc = (d == doc || d == doc.body),\r
6414                 s = d.style;\r
6415 \r
6416             // If the body, use Ext.lib.Dom\r
6417             if (isDoc) {\r
6418                 var extdom = Ext.lib.Dom;\r
6419                 return {\r
6420                     width : extdom.getViewWidth(),\r
6421                     height : extdom.getViewHeight()\r
6422                 }\r
6423             }\r
6424             // Use Styles if they are set\r
6425             if(s.width && s.width != 'auto'){\r
6426                 w = parseFloat(s.width);\r
6427                 if(me.isBorderBox()){\r
6428                    w -= me.getFrameWidth('lr');\r
6429                 }\r
6430             }\r
6431             // Use Styles if they are set\r
6432             if(s.height && s.height != 'auto'){\r
6433                 h = parseFloat(s.height);\r
6434                 if(me.isBorderBox()){\r
6435                    h -= me.getFrameWidth('tb');\r
6436                 }\r
6437             }\r
6438             // Use getWidth/getHeight if style not set.\r
6439             return {width: w || me.getWidth(true), height: h || me.getHeight(true)};\r
6440         },\r
6441 \r
6442         /**\r
6443          * Returns the size of the element.\r
6444          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding\r
6445          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}\r
6446          */\r
6447         getSize : function(contentSize){\r
6448             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};\r
6449         },\r
6450 \r
6451         /**\r
6452          * Forces the browser to repaint this element\r
6453          * @return {Ext.Element} this\r
6454          */\r
6455         repaint : function(){\r
6456             var dom = this.dom;\r
6457             this.addClass("x-repaint");\r
6458             setTimeout(function(){\r
6459                 Ext.fly(dom).removeClass("x-repaint");\r
6460             }, 1);\r
6461             return this;\r
6462         },\r
6463 \r
6464         /**\r
6465          * Disables text selection for this element (normalized across browsers)\r
6466          * @return {Ext.Element} this\r
6467          */\r
6468         unselectable : function(){\r
6469             this.dom.unselectable = "on";\r
6470             return this.swallowEvent("selectstart", true).\r
6471                         applyStyles("-moz-user-select:none;-khtml-user-select:none;").\r
6472                         addClass("x-unselectable");\r
6473         },\r
6474 \r
6475         /**\r
6476          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,\r
6477          * then it returns the calculated width of the sides (see getPadding)\r
6478          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides\r
6479          * @return {Object/Number}\r
6480          */\r
6481         getMargins : function(side){\r
6482             var me = this,\r
6483                 key,\r
6484                 hash = {t:"top", l:"left", r:"right", b: "bottom"},\r
6485                 o = {};\r
6486 \r
6487             if (!side) {\r
6488                 for (key in me.margins){\r
6489                     o[hash[key]] = parseFloat(me.getStyle(me.margins[key])) || 0;\r
6490                 }\r
6491                 return o;\r
6492             } else {\r
6493                 return me.addStyles.call(me, side, me.margins);\r
6494             }\r
6495         }\r
6496     };\r
6497 }());\r
6498 /**\r
6499  * @class Ext.Element\r
6500  */\r
6501 (function(){\r
6502 var D = Ext.lib.Dom,\r
6503         LEFT = "left",\r
6504         RIGHT = "right",\r
6505         TOP = "top",\r
6506         BOTTOM = "bottom",\r
6507         POSITION = "position",\r
6508         STATIC = "static",\r
6509         RELATIVE = "relative",\r
6510         AUTO = "auto",\r
6511         ZINDEX = "z-index";\r
6512 \r
6513 Ext.Element.addMethods({\r
6514         /**\r
6515       * Gets the current X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).\r
6516       * @return {Number} The X position of the element\r
6517       */\r
6518     getX : function(){\r
6519         return D.getX(this.dom);\r
6520     },\r
6521 \r
6522     /**\r
6523       * Gets the current Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).\r
6524       * @return {Number} The Y position of the element\r
6525       */\r
6526     getY : function(){\r
6527         return D.getY(this.dom);\r
6528     },\r
6529 \r
6530     /**\r
6531       * Gets the current position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).\r
6532       * @return {Array} The XY position of the element\r
6533       */\r
6534     getXY : function(){\r
6535         return D.getXY(this.dom);\r
6536     },\r
6537 \r
6538     /**\r
6539       * Returns the offsets of this element from the passed element. Both element must be part of the DOM tree and not have display:none to have page coordinates.\r
6540       * @param {Mixed} element The element to get the offsets from.\r
6541       * @return {Array} The XY page offsets (e.g. [100, -200])\r
6542       */\r
6543     getOffsetsTo : function(el){\r
6544         var o = this.getXY(),\r
6545                 e = Ext.fly(el, '_internal').getXY();\r
6546         return [o[0]-e[0],o[1]-e[1]];\r
6547     },\r
6548 \r
6549     /**\r
6550      * Sets the X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).\r
6551      * @param {Number} The X position of the element\r
6552      * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object\r
6553      * @return {Ext.Element} this\r
6554      */\r
6555     setX : function(x, animate){            \r
6556             return this.setXY([x, this.getY()], this.animTest(arguments, animate, 1));\r
6557     },\r
6558 \r
6559     /**\r
6560      * Sets the Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).\r
6561      * @param {Number} The Y position of the element\r
6562      * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object\r
6563      * @return {Ext.Element} this\r
6564      */\r
6565     setY : function(y, animate){            \r
6566             return this.setXY([this.getX(), y], this.animTest(arguments, animate, 1));\r
6567     },\r
6568 \r
6569     /**\r
6570      * Sets the element's left position directly using CSS style (instead of {@link #setX}).\r
6571      * @param {String} left The left CSS property value\r
6572      * @return {Ext.Element} this\r
6573      */\r
6574     setLeft : function(left){\r
6575         this.setStyle(LEFT, this.addUnits(left));\r
6576         return this;\r
6577     },\r
6578 \r
6579     /**\r
6580      * Sets the element's top position directly using CSS style (instead of {@link #setY}).\r
6581      * @param {String} top The top CSS property value\r
6582      * @return {Ext.Element} this\r
6583      */\r
6584     setTop : function(top){\r
6585         this.setStyle(TOP, this.addUnits(top));\r
6586         return this;\r
6587     },\r
6588 \r
6589     /**\r
6590      * Sets the element's CSS right style.\r
6591      * @param {String} right The right CSS property value\r
6592      * @return {Ext.Element} this\r
6593      */\r
6594     setRight : function(right){\r
6595         this.setStyle(RIGHT, this.addUnits(right));\r
6596         return this;\r
6597     },\r
6598 \r
6599     /**\r
6600      * Sets the element's CSS bottom style.\r
6601      * @param {String} bottom The bottom CSS property value\r
6602      * @return {Ext.Element} this\r
6603      */\r
6604     setBottom : function(bottom){\r
6605         this.setStyle(BOTTOM, this.addUnits(bottom));\r
6606         return this;\r
6607     },\r
6608 \r
6609     /**\r
6610      * Sets the position of the element in page coordinates, regardless of how the element is positioned.\r
6611      * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).\r
6612      * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)\r
6613      * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object\r
6614      * @return {Ext.Element} this\r
6615      */\r
6616     setXY : function(pos, animate){\r
6617             var me = this;\r
6618         if(!animate || !me.anim){\r
6619             D.setXY(me.dom, pos);\r
6620         }else{\r
6621             me.anim({points: {to: pos}}, me.preanim(arguments, 1), 'motion');\r
6622         }\r
6623         return me;\r
6624     },\r
6625 \r
6626     /**\r
6627      * Sets the position of the element in page coordinates, regardless of how the element is positioned.\r
6628      * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).\r
6629      * @param {Number} x X value for new position (coordinates are page-based)\r
6630      * @param {Number} y Y value for new position (coordinates are page-based)\r
6631      * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object\r
6632      * @return {Ext.Element} this\r
6633      */\r
6634     setLocation : function(x, y, animate){\r
6635         return this.setXY([x, y], this.animTest(arguments, animate, 2));\r
6636     },\r
6637 \r
6638     /**\r
6639      * Sets the position of the element in page coordinates, regardless of how the element is positioned.\r
6640      * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).\r
6641      * @param {Number} x X value for new position (coordinates are page-based)\r
6642      * @param {Number} y Y value for new position (coordinates are page-based)\r
6643      * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object\r
6644      * @return {Ext.Element} this\r
6645      */\r
6646     moveTo : function(x, y, animate){\r
6647         return this.setXY([x, y], this.animTest(arguments, animate, 2));        \r
6648     },    \r
6649     \r
6650     /**\r
6651      * Gets the left X coordinate\r
6652      * @param {Boolean} local True to get the local css position instead of page coordinate\r
6653      * @return {Number}\r
6654      */\r
6655     getLeft : function(local){\r
6656             return !local ? this.getX() : parseInt(this.getStyle(LEFT), 10) || 0;\r
6657     },\r
6658 \r
6659     /**\r
6660      * Gets the right X coordinate of the element (element X position + element width)\r
6661      * @param {Boolean} local True to get the local css position instead of page coordinate\r
6662      * @return {Number}\r
6663      */\r
6664     getRight : function(local){\r
6665             var me = this;\r
6666             return !local ? me.getX() + me.getWidth() : (me.getLeft(true) + me.getWidth()) || 0;\r
6667     },\r
6668 \r
6669     /**\r
6670      * Gets the top Y coordinate\r
6671      * @param {Boolean} local True to get the local css position instead of page coordinate\r
6672      * @return {Number}\r
6673      */\r
6674     getTop : function(local) {\r
6675             return !local ? this.getY() : parseInt(this.getStyle(TOP), 10) || 0;\r
6676     },\r
6677 \r
6678     /**\r
6679      * Gets the bottom Y coordinate of the element (element Y position + element height)\r
6680      * @param {Boolean} local True to get the local css position instead of page coordinate\r
6681      * @return {Number}\r
6682      */\r
6683     getBottom : function(local){\r
6684             var me = this;\r
6685             return !local ? me.getY() + me.getHeight() : (me.getTop(true) + me.getHeight()) || 0;\r
6686     },\r
6687 \r
6688     /**\r
6689     * Initializes positioning on this element. If a desired position is not passed, it will make the\r
6690     * the element positioned relative IF it is not already positioned.\r
6691     * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"\r
6692     * @param {Number} zIndex (optional) The zIndex to apply\r
6693     * @param {Number} x (optional) Set the page X position\r
6694     * @param {Number} y (optional) Set the page Y position\r
6695     */\r
6696     position : function(pos, zIndex, x, y){\r
6697             var me = this;\r
6698             \r
6699         if(!pos && me.isStyle(POSITION, STATIC)){           \r
6700             me.setStyle(POSITION, RELATIVE);           \r
6701         } else if(pos) {\r
6702             me.setStyle(POSITION, pos);\r
6703         }\r
6704         if(zIndex){\r
6705             me.setStyle(ZINDEX, zIndex);\r
6706         }\r
6707         if(x || y) me.setXY([x || false, y || false]);\r
6708     },\r
6709 \r
6710     /**\r
6711     * Clear positioning back to the default when the document was loaded\r
6712     * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.\r
6713     * @return {Ext.Element} this\r
6714      */\r
6715     clearPositioning : function(value){\r
6716         value = value || '';\r
6717         this.setStyle({\r
6718             left : value,\r
6719             right : value,\r
6720             top : value,\r
6721             bottom : value,\r
6722             "z-index" : "",\r
6723             position : STATIC\r
6724         });\r
6725         return this;\r
6726     },\r
6727 \r
6728     /**\r
6729     * Gets an object with all CSS positioning properties. Useful along with setPostioning to get\r
6730     * snapshot before performing an update and then restoring the element.\r
6731     * @return {Object}\r
6732     */\r
6733     getPositioning : function(){\r
6734         var l = this.getStyle(LEFT);\r
6735         var t = this.getStyle(TOP);\r
6736         return {\r
6737             "position" : this.getStyle(POSITION),\r
6738             "left" : l,\r
6739             "right" : l ? "" : this.getStyle(RIGHT),\r
6740             "top" : t,\r
6741             "bottom" : t ? "" : this.getStyle(BOTTOM),\r
6742             "z-index" : this.getStyle(ZINDEX)\r
6743         };\r
6744     },\r
6745     \r
6746     /**\r
6747     * Set positioning with an object returned by getPositioning().\r
6748     * @param {Object} posCfg\r
6749     * @return {Ext.Element} this\r
6750      */\r
6751     setPositioning : function(pc){\r
6752             var me = this,\r
6753                 style = me.dom.style;\r
6754                 \r
6755         me.setStyle(pc);\r
6756         \r
6757         if(pc.right == AUTO){\r
6758             style.right = "";\r
6759         }\r
6760         if(pc.bottom == AUTO){\r
6761             style.bottom = "";\r
6762         }\r
6763         \r
6764         return me;\r
6765     },    \r
6766         \r
6767     /**\r
6768      * Translates the passed page coordinates into left/top css values for this element\r
6769      * @param {Number/Array} x The page x or an array containing [x, y]\r
6770      * @param {Number} y (optional) The page y, required if x is not an array\r
6771      * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}\r
6772      */\r
6773     translatePoints : function(x, y){                \r
6774             y = isNaN(x[1]) ? y : x[1];\r
6775         x = isNaN(x[0]) ? x : x[0];\r
6776         var me = this,\r
6777                 relative = me.isStyle(POSITION, RELATIVE),\r
6778                 o = me.getXY(),\r
6779                 l = parseInt(me.getStyle(LEFT), 10),\r
6780                 t = parseInt(me.getStyle(TOP), 10);\r
6781         \r
6782         l = !isNaN(l) ? l : (relative ? 0 : me.dom.offsetLeft);\r
6783         t = !isNaN(t) ? t : (relative ? 0 : me.dom.offsetTop);        \r
6784 \r
6785         return {left: (x - o[0] + l), top: (y - o[1] + t)}; \r
6786     },\r
6787     \r
6788     animTest : function(args, animate, i) {\r
6789         return !!animate && this.preanim ? this.preanim(args, i) : false;\r
6790     }\r
6791 });\r
6792 })();/**\r
6793  * @class Ext.Element\r
6794  */\r
6795 Ext.Element.addMethods({\r
6796     /**\r
6797      * Sets the element's box. Use getBox() on another element to get a box obj. If animate is true then width, height, x and y will be animated concurrently.\r
6798      * @param {Object} box The box to fill {x, y, width, height}\r
6799      * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically\r
6800      * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object\r
6801      * @return {Ext.Element} this\r
6802      */\r
6803     setBox : function(box, adjust, animate){\r
6804         var me = this,\r
6805                 w = box.width, \r
6806                 h = box.height;\r
6807         if((adjust && !me.autoBoxAdjust) && !me.isBorderBox()){\r
6808            w -= (me.getBorderWidth("lr") + me.getPadding("lr"));\r
6809            h -= (me.getBorderWidth("tb") + me.getPadding("tb"));\r
6810         }\r
6811         me.setBounds(box.x, box.y, w, h, me.animTest.call(me, arguments, animate, 2));\r
6812         return me;\r
6813     },\r
6814 \r
6815     /**\r
6816      * Return an object defining the area of this Element which can be passed to {@link #setBox} to\r
6817      * set another Element's size/location to match this element.\r
6818      * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.\r
6819      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.\r
6820      * @return {Object} box An object in the format<pre><code>\r
6821 {\r
6822     x: &lt;Element's X position>,\r
6823     y: &lt;Element's Y position>,\r
6824     width: &lt;Element's width>,\r
6825     height: &lt;Element's height>,\r
6826     bottom: &lt;Element's lower bound>,\r
6827     right: &lt;Element's rightmost bound>\r
6828 }\r
6829 </code></pre>\r
6830      * The returned object may also be addressed as an Array where index 0 contains the X position\r
6831      * and index 1 contains the Y position. So the result may also be used for {@link #setXY}\r
6832      */\r
6833         getBox : function(contentBox, local) {      \r
6834             var me = this,\r
6835                 xy,\r
6836                 left,\r
6837                 top,\r
6838                 getBorderWidth = me.getBorderWidth,\r
6839                 getPadding = me.getPadding, \r
6840                 l,\r
6841                 r,\r
6842                 t,\r
6843                 b;\r
6844         if(!local){\r
6845             xy = me.getXY();\r
6846         }else{\r
6847             left = parseInt(me.getStyle("left"), 10) || 0;\r
6848             top = parseInt(me.getStyle("top"), 10) || 0;\r
6849             xy = [left, top];\r
6850         }\r
6851         var el = me.dom, w = el.offsetWidth, h = el.offsetHeight, bx;\r
6852         if(!contentBox){\r
6853             bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};\r
6854         }else{\r
6855             l = getBorderWidth.call(me, "l") + getPadding.call(me, "l");\r
6856             r = getBorderWidth.call(me, "r") + getPadding.call(me, "r");\r
6857             t = getBorderWidth.call(me, "t") + getPadding.call(me, "t");\r
6858             b = getBorderWidth.call(me, "b") + getPadding.call(me, "b");\r
6859             bx = {x: xy[0]+l, y: xy[1]+t, 0: xy[0]+l, 1: xy[1]+t, width: w-(l+r), height: h-(t+b)};\r
6860         }\r
6861         bx.right = bx.x + bx.width;\r
6862         bx.bottom = bx.y + bx.height;\r
6863         return bx;\r
6864         },\r
6865         \r
6866     /**\r
6867      * Move this element relative to its current position.\r
6868      * @param {String} direction Possible values are: "l" (or "left"), "r" (or "right"), "t" (or "top", or "up"), "b" (or "bottom", or "down").\r
6869      * @param {Number} distance How far to move the element in pixels\r
6870      * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object\r
6871      * @return {Ext.Element} this\r
6872      */\r
6873      move : function(direction, distance, animate){\r
6874         var me = this,          \r
6875                 xy = me.getXY(),\r
6876                 x = xy[0],\r
6877                 y = xy[1],              \r
6878                 left = [x - distance, y],\r
6879                 right = [x + distance, y],\r
6880                 top = [x, y - distance],\r
6881                 bottom = [x, y + distance],\r
6882                 hash = {\r
6883                         l :     left,\r
6884                         left : left,\r
6885                         r : right,\r
6886                         right : right,\r
6887                         t : top,\r
6888                         top : top,\r
6889                         up : top,\r
6890                         b : bottom, \r
6891                         bottom : bottom,\r
6892                         down : bottom                           \r
6893                 };\r
6894         \r
6895             direction = direction.toLowerCase();    \r
6896             me.moveTo(hash[direction][0], hash[direction][1], me.animTest.call(me, arguments, animate, 2));\r
6897     },\r
6898     \r
6899     /**\r
6900      * Quick set left and top adding default units\r
6901      * @param {String} left The left CSS property value\r
6902      * @param {String} top The top CSS property value\r
6903      * @return {Ext.Element} this\r
6904      */\r
6905      setLeftTop : function(left, top){\r
6906             var me = this,\r
6907                 style = me.dom.style;\r
6908         style.left = me.addUnits(left);\r
6909         style.top = me.addUnits(top);\r
6910         return me;\r
6911     },\r
6912     \r
6913     /**\r
6914      * Returns the region of the given element.\r
6915      * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).\r
6916      * @return {Region} A Ext.lib.Region containing "top, left, bottom, right" member data.\r
6917      */\r
6918     getRegion : function(){\r
6919         return Ext.lib.Dom.getRegion(this.dom);\r
6920     },\r
6921     \r
6922     /**\r
6923      * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.\r
6924      * @param {Number} x X value for new position (coordinates are page-based)\r
6925      * @param {Number} y Y value for new position (coordinates are page-based)\r
6926      * @param {Mixed} width The new width. This may be one of:<div class="mdetail-params"><ul>\r
6927      * <li>A Number specifying the new width in this Element's {@link #defaultUnit}s (by default, pixels)</li>\r
6928      * <li>A String used to set the CSS width style. Animation may <b>not</b> be used.\r
6929      * </ul></div>\r
6930      * @param {Mixed} height The new height. This may be one of:<div class="mdetail-params"><ul>\r
6931      * <li>A Number specifying the new height in this Element's {@link #defaultUnit}s (by default, pixels)</li>\r
6932      * <li>A String used to set the CSS height style. Animation may <b>not</b> be used.</li>\r
6933      * </ul></div>\r
6934      * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object\r
6935      * @return {Ext.Element} this\r
6936      */\r
6937     setBounds : function(x, y, width, height, animate){\r
6938             var me = this;\r
6939         if (!animate || !me.anim) {\r
6940             me.setSize(width, height);\r
6941             me.setLocation(x, y);\r
6942         } else {\r
6943             me.anim({points: {to: [x, y]}, \r
6944                          width: {to: me.adjustWidth(width)}, \r
6945                          height: {to: me.adjustHeight(height)}},\r
6946                      me.preanim(arguments, 4), \r
6947                      'motion');\r
6948         }\r
6949         return me;\r
6950     },\r
6951 \r
6952     /**\r
6953      * Sets the element's position and size the specified region. If animation is true then width, height, x and y will be animated concurrently.\r
6954      * @param {Ext.lib.Region} region The region to fill\r
6955      * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object\r
6956      * @return {Ext.Element} this\r
6957      */\r
6958     setRegion : function(region, animate) {\r
6959         return this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.animTest.call(this, arguments, animate, 1));\r
6960     }\r
6961 });/**\r
6962  * @class Ext.Element\r
6963  */\r
6964 Ext.Element.addMethods({\r
6965     /**\r
6966      * Returns true if this element is scrollable.\r
6967      * @return {Boolean}\r
6968      */\r
6969     isScrollable : function(){\r
6970         var dom = this.dom;\r
6971         return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;\r
6972     },\r
6973 \r
6974     /**\r
6975      * Scrolls this element the specified scroll point. It does NOT do bounds checking so if you scroll to a weird value it will try to do it. For auto bounds checking, use scroll().\r
6976      * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.\r
6977      * @param {Number} value The new scroll value.\r
6978      * @return {Element} this\r
6979      */\r
6980     scrollTo : function(side, value){\r
6981         this.dom["scroll" + (/top/i.test(side) ? "Top" : "Left")] = value;\r
6982         return this;\r
6983     },\r
6984 \r
6985     /**\r
6986      * Returns the current scroll position of the element.\r
6987      * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}\r
6988      */\r
6989     getScroll : function(){\r
6990         var d = this.dom, \r
6991             doc = document,\r
6992             body = doc.body,\r
6993             docElement = doc.documentElement,\r
6994             l,\r
6995             t,\r
6996             ret;\r
6997 \r
6998         if(d == doc || d == body){\r
6999             if(Ext.isIE && Ext.isStrict){\r
7000                 l = docElement.scrollLeft; \r
7001                 t = docElement.scrollTop;\r
7002             }else{\r
7003                 l = window.pageXOffset;\r
7004                 t = window.pageYOffset;\r
7005             }\r
7006             ret = {left: l || (body ? body.scrollLeft : 0), top: t || (body ? body.scrollTop : 0)};\r
7007         }else{\r
7008             ret = {left: d.scrollLeft, top: d.scrollTop};\r
7009         }\r
7010         return ret;\r
7011     }\r
7012 });/**\r
7013  * @class Ext.Element\r
7014  */\r
7015 Ext.Element.addMethods({\r
7016     /**\r
7017      * Scrolls this element the specified scroll point. It does NOT do bounds checking so if you scroll to a weird value it will try to do it. For auto bounds checking, use scroll().\r
7018      * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.\r
7019      * @param {Number} value The new scroll value\r
7020      * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object\r
7021      * @return {Element} this\r
7022      */\r
7023     scrollTo : function(side, value, animate){\r
7024         var top = /top/i.test(side), //check if we're scrolling top or left\r
7025                 me = this,\r
7026                 dom = me.dom,\r
7027             prop;\r
7028         if (!animate || !me.anim) {\r
7029             prop = 'scroll' + (top ? 'Top' : 'Left'), // just setting the value, so grab the direction\r
7030             dom[prop] = value;\r
7031         }else{\r
7032             prop = 'scroll' + (top ? 'Left' : 'Top'), // if scrolling top, we need to grab scrollLeft, if left, scrollTop\r
7033             me.anim({scroll: {to: top ? [dom[prop], value] : [value, dom[prop]]}},\r
7034                          me.preanim(arguments, 2), 'scroll');\r
7035         }\r
7036         return me;\r
7037     },\r
7038     \r
7039     /**\r
7040      * Scrolls this element into view within the passed container.\r
7041      * @param {Mixed} container (optional) The container element to scroll (defaults to document.body).  Should be a\r
7042      * string (id), dom node, or Ext.Element.\r
7043      * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)\r
7044      * @return {Ext.Element} this\r
7045      */\r
7046     scrollIntoView : function(container, hscroll){\r
7047         var c = Ext.getDom(container) || Ext.getBody().dom,\r
7048                 el = this.dom,\r
7049                 o = this.getOffsetsTo(c),\r
7050             l = o[0] + c.scrollLeft,\r
7051             t = o[1] + c.scrollTop,\r
7052             b = t + el.offsetHeight,\r
7053             r = l + el.offsetWidth,\r
7054                 ch = c.clientHeight,\r
7055                 ct = parseInt(c.scrollTop, 10),\r
7056                 cl = parseInt(c.scrollLeft, 10),\r
7057                 cb = ct + ch,\r
7058                 cr = cl + c.clientWidth;\r
7059 \r
7060         if (el.offsetHeight > ch || t < ct) {\r
7061                 c.scrollTop = t;\r
7062         } else if (b > cb){\r
7063             c.scrollTop = b-ch;\r
7064         }\r
7065         c.scrollTop = c.scrollTop; // corrects IE, other browsers will ignore\r
7066 \r
7067         if(hscroll !== false){\r
7068                         if(el.offsetWidth > c.clientWidth || l < cl){\r
7069                 c.scrollLeft = l;\r
7070             }else if(r > cr){\r
7071                 c.scrollLeft = r - c.clientWidth;\r
7072             }\r
7073             c.scrollLeft = c.scrollLeft;\r
7074         }\r
7075         return this;\r
7076     },\r
7077 \r
7078     // private\r
7079     scrollChildIntoView : function(child, hscroll){\r
7080         Ext.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);\r
7081     },\r
7082     \r
7083     /**\r
7084      * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is\r
7085      * within this element's scrollable range.\r
7086      * @param {String} direction Possible values are: "l" (or "left"), "r" (or "right"), "t" (or "top", or "up"), "b" (or "bottom", or "down").\r
7087      * @param {Number} distance How far to scroll the element in pixels\r
7088      * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object\r
7089      * @return {Boolean} Returns true if a scroll was triggered or false if the element\r
7090      * was scrolled as far as it could go.\r
7091      */\r
7092      scroll : function(direction, distance, animate){\r
7093          if(!this.isScrollable()){\r
7094              return;\r
7095          }\r
7096          var el = this.dom,\r
7097             l = el.scrollLeft, t = el.scrollTop,\r
7098             w = el.scrollWidth, h = el.scrollHeight,\r
7099             cw = el.clientWidth, ch = el.clientHeight,\r
7100             scrolled = false, v,\r
7101             hash = {\r
7102                 l: Math.min(l + distance, w-cw),\r
7103                 r: v = Math.max(l - distance, 0),\r
7104                 t: Math.max(t - distance, 0),\r
7105                 b: Math.min(t + distance, h-ch)\r
7106             };\r
7107             hash.d = hash.b;\r
7108             hash.u = hash.t;\r
7109             \r
7110          direction = direction.substr(0, 1);\r
7111          if((v = hash[direction]) > -1){\r
7112             scrolled = true;\r
7113             this.scrollTo(direction == 'l' || direction == 'r' ? 'left' : 'top', v, this.preanim(arguments, 2));\r
7114          }\r
7115          return scrolled;\r
7116     }\r
7117 });/**\r
7118  * @class Ext.Element\r
7119  */\r
7120 /**\r
7121  * Visibility mode constant for use with {@link #setVisibilityMode}. Use visibility to hide element\r
7122  * @static\r
7123  * @type Number\r
7124  */\r
7125 Ext.Element.VISIBILITY = 1;\r
7126 /**\r
7127  * Visibility mode constant for use with {@link #setVisibilityMode}. Use display to hide element\r
7128  * @static\r
7129  * @type Number\r
7130  */\r
7131 Ext.Element.DISPLAY = 2;\r
7132 \r
7133 Ext.Element.addMethods(function(){\r
7134     var VISIBILITY = "visibility",\r
7135         DISPLAY = "display",\r
7136         HIDDEN = "hidden",\r
7137         NONE = "none",      \r
7138         ORIGINALDISPLAY = 'originalDisplay',\r
7139         VISMODE = 'visibilityMode',\r
7140         ELDISPLAY = Ext.Element.DISPLAY,\r
7141         data = Ext.Element.data,\r
7142         getDisplay = function(dom){\r
7143             var d = data(dom, ORIGINALDISPLAY);\r
7144             if(d === undefined){\r
7145                 data(dom, ORIGINALDISPLAY, d = '');\r
7146             }\r
7147             return d;\r
7148         },\r
7149         getVisMode = function(dom){\r
7150             var m = data(dom, VISMODE);\r
7151             if(m === undefined){\r
7152                 data(dom, VISMODE, m = 1)\r
7153             }\r
7154             return m;\r
7155         };\r
7156     \r
7157     return {\r
7158         /**\r
7159          * The element's default display mode  (defaults to "")\r
7160          * @type String\r
7161          */\r
7162         originalDisplay : "",\r
7163         visibilityMode : 1,\r
7164         \r
7165         /**\r
7166          * Sets the element's visibility mode. When setVisible() is called it\r
7167          * will use this to determine whether to set the visibility or the display property.\r
7168          * @param {Number} visMode Ext.Element.VISIBILITY or Ext.Element.DISPLAY\r
7169          * @return {Ext.Element} this\r
7170          */\r
7171         setVisibilityMode : function(visMode){  \r
7172             data(this.dom, VISMODE, visMode);\r
7173             return this;\r
7174         },\r
7175         \r
7176         /**\r
7177          * Perform custom animation on this element.\r
7178          * <div><ul class="mdetail-params">\r
7179          * <li><u>Animation Properties</u></li>\r
7180          * \r
7181          * <p>The Animation Control Object enables gradual transitions for any member of an\r
7182          * element's style object that takes a numeric value including but not limited to\r
7183          * these properties:</p><div><ul class="mdetail-params">\r
7184          * <li><tt>bottom, top, left, right</tt></li>\r
7185          * <li><tt>height, width</tt></li>\r
7186          * <li><tt>margin, padding</tt></li>\r
7187          * <li><tt>borderWidth</tt></li>\r
7188          * <li><tt>opacity</tt></li>\r
7189          * <li><tt>fontSize</tt></li>\r
7190          * <li><tt>lineHeight</tt></li>\r
7191          * </ul></div>\r
7192          * \r
7193          * \r
7194          * <li><u>Animation Property Attributes</u></li>\r
7195          * \r
7196          * <p>Each Animation Property is a config object with optional properties:</p>\r
7197          * <div><ul class="mdetail-params">\r
7198          * <li><tt>by</tt>*  : relative change - start at current value, change by this value</li>\r
7199          * <li><tt>from</tt> : ignore current value, start from this value</li>\r
7200          * <li><tt>to</tt>*  : start at current value, go to this value</li>\r
7201          * <li><tt>unit</tt> : any allowable unit specification</li>\r
7202          * <p>* do not specify both <tt>to</tt> and <tt>by</tt> for an animation property</p>\r
7203          * </ul></div>\r
7204          * \r
7205          * <li><u>Animation Types</u></li>\r
7206          * \r
7207          * <p>The supported animation types:</p><div><ul class="mdetail-params">\r
7208          * <li><tt>'run'</tt> : Default\r
7209          * <pre><code>\r
7210 var el = Ext.get('complexEl');\r
7211 el.animate(\r
7212     // animation control object\r
7213     {\r
7214         borderWidth: {to: 3, from: 0},\r
7215         opacity: {to: .3, from: 1},\r
7216         height: {to: 50, from: el.getHeight()},\r
7217         width: {to: 300, from: el.getWidth()},\r
7218         top  : {by: - 100, unit: 'px'},\r
7219     },\r
7220     0.35,      // animation duration\r
7221     null,      // callback\r
7222     'easeOut', // easing method\r
7223     'run'      // animation type ('run','color','motion','scroll')    \r
7224 );\r
7225          * </code></pre>\r
7226          * </li>\r
7227          * <li><tt>'color'</tt>\r
7228          * <p>Animates transition of background, text, or border colors.</p>\r
7229          * <pre><code>\r
7230 el.animate(\r
7231     // animation control object\r
7232     {\r
7233         color: { to: '#06e' },\r
7234         backgroundColor: { to: '#e06' }\r
7235     },\r
7236     0.35,      // animation duration\r
7237     null,      // callback\r
7238     'easeOut', // easing method\r
7239     'color'    // animation type ('run','color','motion','scroll')    \r
7240 );\r
7241          * </code></pre> \r
7242          * </li>\r
7243          * \r
7244          * <li><tt>'motion'</tt>\r
7245          * <p>Animates the motion of an element to/from specific points using optional bezier\r
7246          * way points during transit.</p>\r
7247          * <pre><code>\r
7248 el.animate(\r
7249     // animation control object\r
7250     {\r
7251         borderWidth: {to: 3, from: 0},\r
7252         opacity: {to: .3, from: 1},\r
7253         height: {to: 50, from: el.getHeight()},\r
7254         width: {to: 300, from: el.getWidth()},\r
7255         top  : {by: - 100, unit: 'px'},\r
7256         points: {\r
7257             to: [50, 100],  // go to this point\r
7258             control: [      // optional bezier way points\r
7259                 [ 600, 800],\r
7260                 [-100, 200]\r
7261             ]\r
7262         }\r
7263     },\r
7264     3000,      // animation duration (milliseconds!)\r
7265     null,      // callback\r
7266     'easeOut', // easing method\r
7267     'motion'   // animation type ('run','color','motion','scroll')    \r
7268 );\r
7269          * </code></pre> \r
7270          * </li>\r
7271          * <li><tt>'scroll'</tt>\r
7272          * <p>Animate horizontal or vertical scrolling of an overflowing page element.</p>\r
7273          * <pre><code>\r
7274 el.animate(\r
7275     // animation control object\r
7276     {\r
7277         scroll: {to: [400, 300]}\r
7278     },\r
7279     0.35,      // animation duration\r
7280     null,      // callback\r
7281     'easeOut', // easing method\r
7282     'scroll'   // animation type ('run','color','motion','scroll')    \r
7283 );\r
7284          * </code></pre> \r
7285          * </li>\r
7286          * </ul></div>\r
7287          * \r
7288          * </ul></div>\r
7289          * \r
7290          * @param {Object} args The animation control args\r
7291          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to <tt>.35</tt>)\r
7292          * @param {Function} onComplete (optional) Function to call when animation completes\r
7293          * @param {String} easing (optional) {@link Ext.Fx#easing} method to use (defaults to <tt>'easeOut'</tt>)\r
7294          * @param {String} animType (optional) <tt>'run'</tt> is the default. Can also be <tt>'color'</tt>,\r
7295          * <tt>'motion'</tt>, or <tt>'scroll'</tt>\r
7296          * @return {Ext.Element} this\r
7297          */\r
7298         animate : function(args, duration, onComplete, easing, animType){       \r
7299             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);\r
7300             return this;\r
7301         },\r
7302     \r
7303         /*\r
7304          * @private Internal animation call\r
7305          */\r
7306         anim : function(args, opt, animType, defaultDur, defaultEase, cb){\r
7307             animType = animType || 'run';\r
7308             opt = opt || {};\r
7309             var me = this,              \r
7310                 anim = Ext.lib.Anim[animType](\r
7311                     me.dom, \r
7312                     args,\r
7313                     (opt.duration || defaultDur) || .35,\r
7314                     (opt.easing || defaultEase) || 'easeOut',\r
7315                     function(){\r
7316                         if(cb) cb.call(me);\r
7317                         if(opt.callback) opt.callback.call(opt.scope || me, me, opt);\r
7318                     },\r
7319                     me\r
7320                 );\r
7321             opt.anim = anim;\r
7322             return anim;\r
7323         },\r
7324     \r
7325         // private legacy anim prep\r
7326         preanim : function(a, i){\r
7327             return !a[i] ? false : (Ext.isObject(a[i]) ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});\r
7328         },\r
7329         \r
7330         /**\r
7331          * Checks whether the element is currently visible using both visibility and display properties.         \r
7332          * @return {Boolean} True if the element is currently visible, else false\r
7333          */\r
7334         isVisible : function() {\r
7335             return !this.isStyle(VISIBILITY, HIDDEN) && !this.isStyle(DISPLAY, NONE);\r
7336         },\r
7337         \r
7338         /**\r
7339          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use\r
7340          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.\r
7341          * @param {Boolean} visible Whether the element is visible\r
7342          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object\r
7343          * @return {Ext.Element} this\r
7344          */\r
7345          setVisible : function(visible, animate){\r
7346             var me = this,\r
7347                 dom = me.dom,\r
7348                 isDisplay = getVisMode(this.dom) == ELDISPLAY;\r
7349                 \r
7350             if (!animate || !me.anim) {\r
7351                 if(isDisplay){\r
7352                     me.setDisplayed(visible);\r
7353                 }else{\r
7354                     me.fixDisplay();\r
7355                     dom.style.visibility = visible ? "visible" : HIDDEN;\r
7356                 }\r
7357             }else{\r
7358                 // closure for composites            \r
7359                 if(visible){\r
7360                     me.setOpacity(.01);\r
7361                     me.setVisible(true);\r
7362                 }\r
7363                 me.anim({opacity: { to: (visible?1:0) }},\r
7364                         me.preanim(arguments, 1),\r
7365                         null,\r
7366                         .35,\r
7367                         'easeIn',\r
7368                         function(){\r
7369                              if(!visible){\r
7370                                  dom.style[isDisplay ? DISPLAY : VISIBILITY] = (isDisplay) ? NONE : HIDDEN;                     \r
7371                                  Ext.fly(dom).setOpacity(1);\r
7372                              }\r
7373                         });\r
7374             }\r
7375             return me;\r
7376         },\r
7377     \r
7378         /**\r
7379          * Toggles the element's visibility or display, depending on visibility mode.\r
7380          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object\r
7381          * @return {Ext.Element} this\r
7382          */\r
7383         toggle : function(animate){\r
7384             var me = this;\r
7385             me.setVisible(!me.isVisible(), me.preanim(arguments, 0));\r
7386             return me;\r
7387         },\r
7388     \r
7389         /**\r
7390          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.\r
7391          * @param {Mixed} value Boolean value to display the element using its default display, or a string to set the display directly.\r
7392          * @return {Ext.Element} this\r
7393          */\r
7394         setDisplayed : function(value) {            \r
7395             if(typeof value == "boolean"){\r
7396                value = value ? getDisplay(this.dom) : NONE;\r
7397             }\r
7398             this.setStyle(DISPLAY, value);\r
7399             return this;\r
7400         },\r
7401         \r
7402         // private\r
7403         fixDisplay : function(){\r
7404             var me = this;\r
7405             if(me.isStyle(DISPLAY, NONE)){\r
7406                 me.setStyle(VISIBILITY, HIDDEN);\r
7407                 me.setStyle(DISPLAY, getDisplay(this.dom)); // first try reverting to default\r
7408                 if(me.isStyle(DISPLAY, NONE)){ // if that fails, default to block\r
7409                     me.setStyle(DISPLAY, "block");\r
7410                 }\r
7411             }\r
7412         },\r
7413     \r
7414         /**\r
7415          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.\r
7416          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object\r
7417          * @return {Ext.Element} this\r
7418          */\r
7419         hide : function(animate){\r
7420             this.setVisible(false, this.preanim(arguments, 0));\r
7421             return this;\r
7422         },\r
7423     \r
7424         /**\r
7425         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.\r
7426         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object\r
7427          * @return {Ext.Element} this\r
7428          */\r
7429         show : function(animate){\r
7430             this.setVisible(true, this.preanim(arguments, 0));\r
7431             return this;\r
7432         }\r
7433     }\r
7434 }());/**\r
7435  * @class Ext.Element\r
7436  */\r
7437 Ext.Element.addMethods(\r
7438 function(){\r
7439     var VISIBILITY = "visibility",\r
7440         DISPLAY = "display",\r
7441         HIDDEN = "hidden",\r
7442         NONE = "none",\r
7443             XMASKED = "x-masked",\r
7444                 XMASKEDRELATIVE = "x-masked-relative",\r
7445         data = Ext.Element.data;\r
7446 \r
7447         return {\r
7448                 /**\r
7449              * Checks whether the element is currently visible using both visibility and display properties.\r
7450              * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)\r
7451              * @return {Boolean} True if the element is currently visible, else false\r
7452              */\r
7453             isVisible : function(deep) {\r
7454                 var vis = !this.isStyle(VISIBILITY,HIDDEN) && !this.isStyle(DISPLAY,NONE),\r
7455                         p = this.dom.parentNode;\r
7456                 if(deep !== true || !vis){\r
7457                     return vis;\r
7458                 }\r
7459                 while(p && !/^body/i.test(p.tagName)){\r
7460                     if(!Ext.fly(p, '_isVisible').isVisible()){\r
7461                         return false;\r
7462                     }\r
7463                     p = p.parentNode;\r
7464                 }\r
7465                 return true;\r
7466             },\r
7467 \r
7468             /**\r
7469              * Returns true if display is not "none"\r
7470              * @return {Boolean}\r
7471              */\r
7472             isDisplayed : function() {\r
7473                 return !this.isStyle(DISPLAY, NONE);\r
7474             },\r
7475 \r
7476                 /**\r
7477              * Convenience method for setVisibilityMode(Element.DISPLAY)\r
7478              * @param {String} display (optional) What to set display to when visible\r
7479              * @return {Ext.Element} this\r
7480              */\r
7481             enableDisplayMode : function(display){\r
7482                 this.setVisibilityMode(Ext.Element.DISPLAY);\r
7483                 if(!Ext.isEmpty(display)){\r
7484                 data(this.dom, 'originalDisplay', display);\r
7485             }\r
7486                 return this;\r
7487             },\r
7488 \r
7489                 /**\r
7490              * Puts a mask over this element to disable user interaction. Requires core.css.\r
7491              * This method can only be applied to elements which accept child nodes.\r
7492              * @param {String} msg (optional) A message to display in the mask\r
7493              * @param {String} msgCls (optional) A css class to apply to the msg element\r
7494              * @return {Element} The mask element\r
7495              */\r
7496             mask : function(msg, msgCls){\r
7497                     var me = this,\r
7498                         dom = me.dom,\r
7499                         dh = Ext.DomHelper,\r
7500                         EXTELMASKMSG = "ext-el-mask-msg",\r
7501                 el,\r
7502                 mask;\r
7503 \r
7504                 if(me.getStyle("position") == "static"){\r
7505                     me.addClass(XMASKEDRELATIVE);\r
7506                 }\r
7507                 if((el = data(dom, 'maskMsg'))){\r
7508                     el.remove();\r
7509                 }\r
7510                 if((el = data(dom, 'mask'))){\r
7511                     el.remove();\r
7512                 }\r
7513 \r
7514             mask = dh.append(dom, {cls : "ext-el-mask"}, true);\r
7515                 data(dom, 'mask', mask);\r
7516 \r
7517                 me.addClass(XMASKED);\r
7518                 mask.setDisplayed(true);\r
7519                 if(typeof msg == 'string'){\r
7520                 var mm = dh.append(dom, {cls : EXTELMASKMSG, cn:{tag:'div'}}, true);\r
7521                 data(dom, 'maskMsg', mm);\r
7522                     mm.dom.className = msgCls ? EXTELMASKMSG + " " + msgCls : EXTELMASKMSG;\r
7523                     mm.dom.firstChild.innerHTML = msg;\r
7524                     mm.setDisplayed(true);\r
7525                     mm.center(me);\r
7526                 }\r
7527                 if(Ext.isIE && !(Ext.isIE7 && Ext.isStrict) && me.getStyle('height') == 'auto'){ // ie will not expand full height automatically\r
7528                     mask.setSize(undefined, me.getHeight());\r
7529                 }\r
7530                 return mask;\r
7531             },\r
7532 \r
7533             /**\r
7534              * Removes a previously applied mask.\r
7535              */\r
7536             unmask : function(){\r
7537                     var me = this,\r
7538                 dom = me.dom,\r
7539                         mask = data(dom, 'mask'),\r
7540                         maskMsg = data(dom, 'maskMsg');\r
7541                 if(mask){\r
7542                     if(maskMsg){\r
7543                         maskMsg.remove();\r
7544                     data(dom, 'maskMsg', undefined);\r
7545                     }\r
7546                     mask.remove();\r
7547                 data(dom, 'mask', undefined);\r
7548                 }\r
7549                 me.removeClass([XMASKED, XMASKEDRELATIVE]);\r
7550             },\r
7551 \r
7552             /**\r
7553              * Returns true if this element is masked\r
7554              * @return {Boolean}\r
7555              */\r
7556             isMasked : function(){\r
7557             var m = data(this.dom, 'mask');\r
7558                 return m && m.isVisible();\r
7559             },\r
7560 \r
7561             /**\r
7562              * Creates an iframe shim for this element to keep selects and other windowed objects from\r
7563              * showing through.\r
7564              * @return {Ext.Element} The new shim element\r
7565              */\r
7566             createShim : function(){\r
7567                 var el = document.createElement('iframe'),\r
7568                         shim;\r
7569                 el.frameBorder = '0';\r
7570                 el.className = 'ext-shim';\r
7571                 el.src = Ext.SSL_SECURE_URL;\r
7572                 shim = Ext.get(this.dom.parentNode.insertBefore(el, this.dom));\r
7573                 shim.autoBoxAdjust = false;\r
7574                 return shim;\r
7575             }\r
7576     };\r
7577 }());/**\r
7578  * @class Ext.Element\r
7579  */\r
7580 Ext.Element.addMethods({\r
7581     /**\r
7582      * Convenience method for constructing a KeyMap\r
7583      * @param {Number/Array/Object/String} key Either a string with the keys to listen for, the numeric key code, array of key codes or an object with the following options:\r
7584      * <code>{key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}</code>\r
7585      * @param {Function} fn The function to call\r
7586      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the specified function is executed. Defaults to this Element.\r
7587      * @return {Ext.KeyMap} The KeyMap created\r
7588      */\r
7589     addKeyListener : function(key, fn, scope){\r
7590         var config;\r
7591         if(!Ext.isObject(key) || Ext.isArray(key)){\r
7592             config = {\r
7593                 key: key,\r
7594                 fn: fn,\r
7595                 scope: scope\r
7596             };\r
7597         }else{\r
7598             config = {\r
7599                 key : key.key,\r
7600                 shift : key.shift,\r
7601                 ctrl : key.ctrl,\r
7602                 alt : key.alt,\r
7603                 fn: fn,\r
7604                 scope: scope\r
7605             };\r
7606         }\r
7607         return new Ext.KeyMap(this, config);\r
7608     },\r
7609 \r
7610     /**\r
7611      * Creates a KeyMap for this element\r
7612      * @param {Object} config The KeyMap config. See {@link Ext.KeyMap} for more details\r
7613      * @return {Ext.KeyMap} The KeyMap created\r
7614      */\r
7615     addKeyMap : function(config){\r
7616         return new Ext.KeyMap(this, config);\r
7617     }\r
7618 });(function(){\r
7619     // contants\r
7620     var NULL = null,\r
7621         UNDEFINED = undefined,\r
7622         TRUE = true,\r
7623         FALSE = false,\r
7624         SETX = "setX",\r
7625         SETY = "setY",\r
7626         SETXY = "setXY",\r
7627         LEFT = "left",\r
7628         BOTTOM = "bottom",\r
7629         TOP = "top",\r
7630         RIGHT = "right",\r
7631         HEIGHT = "height",\r
7632         WIDTH = "width",\r
7633         POINTS = "points",\r
7634         HIDDEN = "hidden",\r
7635         ABSOLUTE = "absolute",\r
7636         VISIBLE = "visible",\r
7637         MOTION = "motion",\r
7638         POSITION = "position",\r
7639         EASEOUT = "easeOut",\r
7640         /*\r
7641          * Use a light flyweight here since we are using so many callbacks and are always assured a DOM element\r
7642          */\r
7643         flyEl = new Ext.Element.Flyweight(),\r
7644         queues = {},\r
7645         getObject = function(o){\r
7646             return o || {};\r
7647         },\r
7648         fly = function(dom){\r
7649             flyEl.dom = dom;\r
7650             flyEl.id = Ext.id(dom);\r
7651             return flyEl;\r
7652         },\r
7653         /*\r
7654          * Queueing now stored outside of the element due to closure issues\r
7655          */\r
7656         getQueue = function(id){\r
7657             if(!queues[id]){\r
7658                 queues[id] = [];\r
7659             }\r
7660             return queues[id];\r
7661         },\r
7662         setQueue = function(id, value){\r
7663             queues[id] = value;\r
7664         };\r
7665         \r
7666 //Notifies Element that fx methods are available\r
7667 Ext.enableFx = TRUE;\r
7668 \r
7669 /**\r
7670  * @class Ext.Fx\r
7671  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied\r
7672  * to the {@link Ext.Element} interface when included, so all effects calls should be performed via {@link Ext.Element}.\r
7673  * Conversely, since the effects are not actually defined in {@link Ext.Element}, Ext.Fx <b>must</b> be\r
7674  * {@link Ext#enableFx included} in order for the Element effects to work.</p><br/>\r
7675  * \r
7676  * <p><b><u>Method Chaining</u></b></p>\r
7677  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that\r
7678  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single\r
7679  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.\r
7680  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,\r
7681  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the\r
7682  * expected results and should be done with care.  Also see <tt>{@link #callback}</tt>.</p><br/>\r
7683  *\r
7684  * <p><b><u>Anchor Options for Motion Effects</u></b></p>\r
7685  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element\r
7686  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>\r
7687 <pre>\r
7688 Value  Description\r
7689 -----  -----------------------------\r
7690 tl     The top left corner\r
7691 t      The center of the top edge\r
7692 tr     The top right corner\r
7693 l      The center of the left edge\r
7694 r      The center of the right edge\r
7695 bl     The bottom left corner\r
7696 b      The center of the bottom edge\r
7697 br     The bottom right corner\r
7698 </pre>\r
7699  * <b>Note</b>: some Fx methods accept specific custom config parameters.  The options shown in the Config Options\r
7700  * section below are common options that can be passed to any Fx method unless otherwise noted.</b>\r
7701  * \r
7702  * @cfg {Function} callback A function called when the effect is finished.  Note that effects are queued internally by the\r
7703  * Fx class, so a callback is not required to specify another effect -- effects can simply be chained together\r
7704  * and called in sequence (see note for <b><u>Method Chaining</u></b> above), for example:<pre><code>\r
7705  * el.slideIn().highlight();\r
7706  * </code></pre>\r
7707  * The callback is intended for any additional code that should run once a particular effect has completed. The Element\r
7708  * being operated upon is passed as the first parameter.\r
7709  * \r
7710  * @cfg {Object} scope The scope (<code>this</code> reference) in which the <tt>{@link #callback}</tt> function is executed. Defaults to the browser window.\r
7711  * \r
7712  * @cfg {String} easing A valid Ext.lib.Easing value for the effect:</p><div class="mdetail-params"><ul>\r
7713  * <li><b><tt>backBoth</tt></b></li>\r
7714  * <li><b><tt>backIn</tt></b></li>\r
7715  * <li><b><tt>backOut</tt></b></li>\r
7716  * <li><b><tt>bounceBoth</tt></b></li>\r
7717  * <li><b><tt>bounceIn</tt></b></li>\r
7718  * <li><b><tt>bounceOut</tt></b></li>\r
7719  * <li><b><tt>easeBoth</tt></b></li>\r
7720  * <li><b><tt>easeBothStrong</tt></b></li>\r
7721  * <li><b><tt>easeIn</tt></b></li>\r
7722  * <li><b><tt>easeInStrong</tt></b></li>\r
7723  * <li><b><tt>easeNone</tt></b></li>\r
7724  * <li><b><tt>easeOut</tt></b></li>\r
7725  * <li><b><tt>easeOutStrong</tt></b></li>\r
7726  * <li><b><tt>elasticBoth</tt></b></li>\r
7727  * <li><b><tt>elasticIn</tt></b></li>\r
7728  * <li><b><tt>elasticOut</tt></b></li>\r
7729  * </ul></div>\r
7730  *\r
7731  * @cfg {String} afterCls A css class to apply after the effect\r
7732  * @cfg {Number} duration The length of time (in seconds) that the effect should last\r
7733  * \r
7734  * @cfg {Number} endOpacity Only applicable for {@link #fadeIn} or {@link #fadeOut}, a number between\r
7735  * <tt>0</tt> and <tt>1</tt> inclusive to configure the ending opacity value.\r
7736  *  \r
7737  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes\r
7738  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to \r
7739  * effects that end with the element being visually hidden, ignored otherwise)\r
7740  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. <tt>"width:100px"</tt>, or an object\r
7741  * in the form <tt>{width:"100px"}</tt>, or a function which returns such a specification that will be applied to the\r
7742  * Element after the effect finishes.\r
7743  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs\r
7744  * @cfg {Boolean} concurrent Whether to allow subsequently-queued effects to run at the same time as the current effect, or to ensure that they run in sequence\r
7745  * @cfg {Boolean} stopFx Whether preceding effects should be stopped and removed before running current effect (only applies to non blocking effects)\r
7746  */\r
7747 Ext.Fx = {\r
7748     \r
7749     // private - calls the function taking arguments from the argHash based on the key.  Returns the return value of the function.\r
7750     //           this is useful for replacing switch statements (for example).\r
7751     switchStatements : function(key, fn, argHash){\r
7752         return fn.apply(this, argHash[key]);\r
7753     },\r
7754     \r
7755     /**\r
7756      * Slides the element into view.  An anchor point can be optionally passed to set the point of\r
7757      * origin for the slide effect.  This function automatically handles wrapping the element with\r
7758      * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.\r
7759      * Usage:\r
7760      *<pre><code>\r
7761 // default: slide the element in from the top\r
7762 el.slideIn();\r
7763 \r
7764 // custom: slide the element in from the right with a 2-second duration\r
7765 el.slideIn('r', { duration: 2 });\r
7766 \r
7767 // common config options shown with default values\r
7768 el.slideIn('t', {\r
7769     easing: 'easeOut',\r
7770     duration: .5\r
7771 });\r
7772 </code></pre>\r
7773      * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')\r
7774      * @param {Object} options (optional) Object literal with any of the Fx config options\r
7775      * @return {Ext.Element} The Element\r
7776      */\r
7777     slideIn : function(anchor, o){ \r
7778         o = getObject(o);\r
7779         var me = this,\r
7780             dom = me.dom,\r
7781             st = dom.style,\r
7782             xy,\r
7783             r,\r
7784             b,              \r
7785             wrap,               \r
7786             after,\r
7787             st,\r
7788             args, \r
7789             pt,\r
7790             bw,\r
7791             bh;\r
7792             \r
7793         anchor = anchor || "t";\r
7794 \r
7795         me.queueFx(o, function(){            \r
7796             xy = fly(dom).getXY();\r
7797             // fix display to visibility\r
7798             fly(dom).fixDisplay();            \r
7799             \r
7800             // restore values after effect\r
7801             r = fly(dom).getFxRestore();      \r
7802             b = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: dom.offsetWidth, height: dom.offsetHeight};\r
7803             b.right = b.x + b.width;\r
7804             b.bottom = b.y + b.height;\r
7805             \r
7806             // fixed size for slide\r
7807             fly(dom).setWidth(b.width).setHeight(b.height);            \r
7808             \r
7809             // wrap if needed\r
7810             wrap = fly(dom).fxWrap(r.pos, o, HIDDEN);\r
7811             \r
7812             st.visibility = VISIBLE;\r
7813             st.position = ABSOLUTE;\r
7814             \r
7815             // clear out temp styles after slide and unwrap\r
7816             function after(){\r
7817                  fly(dom).fxUnwrap(wrap, r.pos, o);\r
7818                  st.width = r.width;\r
7819                  st.height = r.height;\r
7820                  fly(dom).afterFx(o);\r
7821             }\r
7822             \r
7823             // time to calculate the positions        \r
7824             pt = {to: [b.x, b.y]}; \r
7825             bw = {to: b.width};\r
7826             bh = {to: b.height};\r
7827                 \r
7828             function argCalc(wrap, style, ww, wh, sXY, sXYval, s1, s2, w, h, p){                    \r
7829                 var ret = {};\r
7830                 fly(wrap).setWidth(ww).setHeight(wh);\r
7831                 if(fly(wrap)[sXY]){\r
7832                     fly(wrap)[sXY](sXYval);                  \r
7833                 }\r
7834                 style[s1] = style[s2] = "0";                    \r
7835                 if(w){\r
7836                     ret.width = w\r
7837                 };\r
7838                 if(h){\r
7839                     ret.height = h;\r
7840                 }\r
7841                 if(p){\r
7842                     ret.points = p;\r
7843                 }\r
7844                 return ret;\r
7845             };\r
7846 \r
7847             args = fly(dom).switchStatements(anchor.toLowerCase(), argCalc, {\r
7848                     t  : [wrap, st, b.width, 0, NULL, NULL, LEFT, BOTTOM, NULL, bh, NULL],\r
7849                     l  : [wrap, st, 0, b.height, NULL, NULL, RIGHT, TOP, bw, NULL, NULL],\r
7850                     r  : [wrap, st, b.width, b.height, SETX, b.right, LEFT, TOP, NULL, NULL, pt],\r
7851                     b  : [wrap, st, b.width, b.height, SETY, b.bottom, LEFT, TOP, NULL, bh, pt],\r
7852                     tl : [wrap, st, 0, 0, NULL, NULL, RIGHT, BOTTOM, bw, bh, pt],\r
7853                     bl : [wrap, st, 0, 0, SETY, b.y + b.height, RIGHT, TOP, bw, bh, pt],\r
7854                     br : [wrap, st, 0, 0, SETXY, [b.right, b.bottom], LEFT, TOP, bw, bh, pt],\r
7855                     tr : [wrap, st, 0, 0, SETX, b.x + b.width, LEFT, BOTTOM, bw, bh, pt]\r
7856                 });\r
7857             \r
7858             st.visibility = VISIBLE;\r
7859             fly(wrap).show();\r
7860 \r
7861             arguments.callee.anim = fly(wrap).fxanim(args,\r
7862                 o,\r
7863                 MOTION,\r
7864                 .5,\r
7865                 EASEOUT, \r
7866                 after);\r
7867         });\r
7868         return me;\r
7869     },\r
7870     \r
7871     /**\r
7872      * Slides the element out of view.  An anchor point can be optionally passed to set the end point\r
7873      * for the slide effect.  When the effect is completed, the element will be hidden (visibility = \r
7874      * 'hidden') but block elements will still take up space in the document.  The element must be removed\r
7875      * from the DOM using the 'remove' config option if desired.  This function automatically handles \r
7876      * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.\r
7877      * Usage:\r
7878      *<pre><code>\r
7879 // default: slide the element out to the top\r
7880 el.slideOut();\r
7881 \r
7882 // custom: slide the element out to the right with a 2-second duration\r
7883 el.slideOut('r', { duration: 2 });\r
7884 \r
7885 // common config options shown with default values\r
7886 el.slideOut('t', {\r
7887     easing: 'easeOut',\r
7888     duration: .5,\r
7889     remove: false,\r
7890     useDisplay: false\r
7891 });\r
7892 </code></pre>\r
7893      * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')\r
7894      * @param {Object} options (optional) Object literal with any of the Fx config options\r
7895      * @return {Ext.Element} The Element\r
7896      */\r
7897     slideOut : function(anchor, o){\r
7898         o = getObject(o);\r
7899         var me = this,\r
7900             dom = me.dom,\r
7901             st = dom.style,\r
7902             xy = me.getXY(),\r
7903             wrap,\r
7904             r,\r
7905             b,\r
7906             a,\r
7907             zero = {to: 0}; \r
7908                     \r
7909         anchor = anchor || "t";\r
7910 \r
7911         me.queueFx(o, function(){\r
7912             \r
7913             // restore values after effect\r
7914             r = fly(dom).getFxRestore(); \r
7915             b = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: dom.offsetWidth, height: dom.offsetHeight};\r
7916             b.right = b.x + b.width;\r
7917             b.bottom = b.y + b.height;\r
7918                 \r
7919             // fixed size for slide   \r
7920             fly(dom).setWidth(b.width).setHeight(b.height);\r
7921 \r
7922             // wrap if needed\r
7923             wrap = fly(dom).fxWrap(r.pos, o, VISIBLE);\r
7924                 \r
7925             st.visibility = VISIBLE;\r
7926             st.position = ABSOLUTE;\r
7927             fly(wrap).setWidth(b.width).setHeight(b.height);            \r
7928 \r
7929             function after(){\r
7930                 o.useDisplay ? fly(dom).setDisplayed(FALSE) : fly(dom).hide();                \r
7931                 fly(dom).fxUnwrap(wrap, r.pos, o);\r
7932                 st.width = r.width;\r
7933                 st.height = r.height;\r
7934                 fly(dom).afterFx(o);\r
7935             }            \r
7936             \r
7937             function argCalc(style, s1, s2, p1, v1, p2, v2, p3, v3){                    \r
7938                 var ret = {};\r
7939                 \r
7940                 style[s1] = style[s2] = "0";\r
7941                 ret[p1] = v1;               \r
7942                 if(p2){\r
7943                     ret[p2] = v2;               \r
7944                 }\r
7945                 if(p3){\r
7946                     ret[p3] = v3;\r
7947                 }\r
7948                 \r
7949                 return ret;\r
7950             };\r
7951             \r
7952             a = fly(dom).switchStatements(anchor.toLowerCase(), argCalc, {\r
7953                 t  : [st, LEFT, BOTTOM, HEIGHT, zero],\r
7954                 l  : [st, RIGHT, TOP, WIDTH, zero],\r
7955                 r  : [st, LEFT, TOP, WIDTH, zero, POINTS, {to : [b.right, b.y]}],\r
7956                 b  : [st, LEFT, TOP, HEIGHT, zero, POINTS, {to : [b.x, b.bottom]}],\r
7957                 tl : [st, RIGHT, BOTTOM, WIDTH, zero, HEIGHT, zero],\r
7958                 bl : [st, RIGHT, TOP, WIDTH, zero, HEIGHT, zero, POINTS, {to : [b.x, b.bottom]}],\r
7959                 br : [st, LEFT, TOP, WIDTH, zero, HEIGHT, zero, POINTS, {to : [b.x + b.width, b.bottom]}],\r
7960                 tr : [st, LEFT, BOTTOM, WIDTH, zero, HEIGHT, zero, POINTS, {to : [b.right, b.y]}]\r
7961             });\r
7962             \r
7963             arguments.callee.anim = fly(wrap).fxanim(a,\r
7964                 o,\r
7965                 MOTION,\r
7966                 .5,\r
7967                 EASEOUT, \r
7968                 after);\r
7969         });\r
7970         return me;\r
7971     },\r
7972 \r
7973     /**\r
7974      * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the \r
7975      * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. \r
7976      * The element must be removed from the DOM using the 'remove' config option if desired.\r
7977      * Usage:\r
7978      *<pre><code>\r
7979 // default\r
7980 el.puff();\r
7981 \r
7982 // common config options shown with default values\r
7983 el.puff({\r
7984     easing: 'easeOut',\r
7985     duration: .5,\r
7986     remove: false,\r
7987     useDisplay: false\r
7988 });\r
7989 </code></pre>\r
7990      * @param {Object} options (optional) Object literal with any of the Fx config options\r
7991      * @return {Ext.Element} The Element\r
7992      */\r
7993     puff : function(o){\r
7994         o = getObject(o);\r
7995         var me = this,\r
7996             dom = me.dom,\r
7997             st = dom.style,\r
7998             width,\r
7999             height,\r
8000             r;\r
8001 \r
8002         me.queueFx(o, function(){\r
8003             width = fly(dom).getWidth();\r
8004             height = fly(dom).getHeight();\r
8005             fly(dom).clearOpacity();\r
8006             fly(dom).show();\r
8007 \r
8008             // restore values after effect\r
8009             r = fly(dom).getFxRestore();                   \r
8010             \r
8011             function after(){\r
8012                 o.useDisplay ? fly(dom).setDisplayed(FALSE) : fly(dom).hide();                  \r
8013                 fly(dom).clearOpacity();  \r
8014                 fly(dom).setPositioning(r.pos);\r
8015                 st.width = r.width;\r
8016                 st.height = r.height;\r
8017                 st.fontSize = '';\r
8018                 fly(dom).afterFx(o);\r
8019             }   \r
8020 \r
8021             arguments.callee.anim = fly(dom).fxanim({\r
8022                     width : {to : fly(dom).adjustWidth(width * 2)},\r
8023                     height : {to : fly(dom).adjustHeight(height * 2)},\r
8024                     points : {by : [-width * .5, -height * .5]},\r
8025                     opacity : {to : 0},\r
8026                     fontSize: {to : 200, unit: "%"}\r
8027                 },\r
8028                 o,\r
8029                 MOTION,\r
8030                 .5,\r
8031                 EASEOUT,\r
8032                  after);\r
8033         });\r
8034         return me;\r
8035     },\r
8036 \r
8037     /**\r
8038      * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).\r
8039      * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still \r
8040      * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.\r
8041      * Usage:\r
8042      *<pre><code>\r
8043 // default\r
8044 el.switchOff();\r
8045 \r
8046 // all config options shown with default values\r
8047 el.switchOff({\r
8048     easing: 'easeIn',\r
8049     duration: .3,\r
8050     remove: false,\r
8051     useDisplay: false\r
8052 });\r
8053 </code></pre>\r
8054      * @param {Object} options (optional) Object literal with any of the Fx config options\r
8055      * @return {Ext.Element} The Element\r
8056      */\r
8057     switchOff : function(o){\r
8058         o = getObject(o);\r
8059         var me = this,\r
8060             dom = me.dom,\r
8061             st = dom.style,\r
8062             r;\r
8063 \r
8064         me.queueFx(o, function(){\r
8065             fly(dom).clearOpacity();\r
8066             fly(dom).clip();\r
8067 \r
8068             // restore values after effect\r
8069             r = fly(dom).getFxRestore();\r
8070                 \r
8071             function after(){\r
8072                 o.useDisplay ? fly(dom).setDisplayed(FALSE) : fly(dom).hide();  \r
8073                 fly(dom).clearOpacity();\r
8074                 fly(dom).setPositioning(r.pos);\r
8075                 st.width = r.width;\r
8076                 st.height = r.height;   \r
8077                 fly(dom).afterFx(o);\r
8078             };\r
8079 \r
8080             fly(dom).fxanim({opacity : {to : 0.3}}, \r
8081                 NULL, \r
8082                 NULL, \r
8083                 .1, \r
8084                 NULL, \r
8085                 function(){                                 \r
8086                     fly(dom).clearOpacity();\r
8087                         (function(){                            \r
8088                             fly(dom).fxanim({\r
8089                                 height : {to : 1},\r
8090                                 points : {by : [0, fly(dom).getHeight() * .5]}\r
8091                             }, \r
8092                             o, \r
8093                             MOTION, \r
8094                             0.3, \r
8095                             'easeIn', \r
8096                             after);\r
8097                         }).defer(100);\r
8098                 });\r
8099         });\r
8100         return me;\r
8101     },\r
8102 \r
8103     /**\r
8104      * Highlights the Element by setting a color (applies to the background-color by default, but can be\r
8105      * changed using the "attr" config option) and then fading back to the original color. If no original\r
8106      * color is available, you should provide the "endColor" config option which will be cleared after the animation.\r
8107      * Usage:\r
8108 <pre><code>\r
8109 // default: highlight background to yellow\r
8110 el.highlight();\r
8111 \r
8112 // custom: highlight foreground text to blue for 2 seconds\r
8113 el.highlight("0000ff", { attr: 'color', duration: 2 });\r
8114 \r
8115 // common config options shown with default values\r
8116 el.highlight("ffff9c", {\r
8117     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value\r
8118     endColor: (current color) or "ffffff",\r
8119     easing: 'easeIn',\r
8120     duration: 1\r
8121 });\r
8122 </code></pre>\r
8123      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')\r
8124      * @param {Object} options (optional) Object literal with any of the Fx config options\r
8125      * @return {Ext.Element} The Element\r
8126      */ \r
8127     highlight : function(color, o){\r
8128         o = getObject(o);\r
8129         var me = this,\r
8130             dom = me.dom,\r
8131             attr = o.attr || "backgroundColor",\r
8132             a = {},\r
8133             restore;\r
8134 \r
8135         me.queueFx(o, function(){\r
8136             fly(dom).clearOpacity();\r
8137             fly(dom).show();\r
8138 \r
8139             function after(){\r
8140                 dom.style[attr] = restore;\r
8141                 fly(dom).afterFx(o);\r
8142             }            \r
8143             restore = dom.style[attr];\r
8144             a[attr] = {from: color || "ffff9c", to: o.endColor || fly(dom).getColor(attr) || "ffffff"};\r
8145             arguments.callee.anim = fly(dom).fxanim(a,\r
8146                 o,\r
8147                 'color',\r
8148                 1,\r
8149                 'easeIn', \r
8150                 after);\r
8151         });\r
8152         return me;\r
8153     },\r
8154 \r
8155    /**\r
8156     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.\r
8157     * Usage:\r
8158 <pre><code>\r
8159 // default: a single light blue ripple\r
8160 el.frame();\r
8161 \r
8162 // custom: 3 red ripples lasting 3 seconds total\r
8163 el.frame("ff0000", 3, { duration: 3 });\r
8164 \r
8165 // common config options shown with default values\r
8166 el.frame("C3DAF9", 1, {\r
8167     duration: 1 //duration of each individual ripple.\r
8168     // Note: Easing is not configurable and will be ignored if included\r
8169 });\r
8170 </code></pre>\r
8171     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').\r
8172     * @param {Number} count (optional) The number of ripples to display (defaults to 1)\r
8173     * @param {Object} options (optional) Object literal with any of the Fx config options\r
8174     * @return {Ext.Element} The Element\r
8175     */\r
8176     frame : function(color, count, o){\r
8177         o = getObject(o);\r
8178         var me = this,\r
8179             dom = me.dom,\r
8180             proxy,\r
8181             active;\r
8182 \r
8183         me.queueFx(o, function(){\r
8184             color = color || '#C3DAF9';\r
8185             if(color.length == 6){\r
8186                 color = '#' + color;\r
8187             }            \r
8188             count = count || 1;\r
8189             fly(dom).show();\r
8190 \r
8191             var xy = fly(dom).getXY(),\r
8192                 b = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: dom.offsetWidth, height: dom.offsetHeight},\r
8193                 queue = function(){\r
8194                     proxy = fly(document.body || document.documentElement).createChild({\r
8195                         style:{\r
8196                             position : ABSOLUTE,\r
8197                             'z-index': 35000, // yee haw\r
8198                             border : '0px solid ' + color\r
8199                         }\r
8200                     });\r
8201                     return proxy.queueFx({}, animFn);\r
8202                 };\r
8203             \r
8204             \r
8205             arguments.callee.anim = {\r
8206                 isAnimated: true,\r
8207                 stop: function() {\r
8208                     count = 0;\r
8209                     proxy.stopFx();\r
8210                 }\r
8211             };\r
8212             \r
8213             function animFn(){\r
8214                 var scale = Ext.isBorderBox ? 2 : 1;\r
8215                 active = proxy.anim({\r
8216                     top : {from : b.y, to : b.y - 20},\r
8217                     left : {from : b.x, to : b.x - 20},\r
8218                     borderWidth : {from : 0, to : 10},\r
8219                     opacity : {from : 1, to : 0},\r
8220                     height : {from : b.height, to : b.height + 20 * scale},\r
8221                     width : {from : b.width, to : b.width + 20 * scale}\r
8222                 },{\r
8223                     duration: o.duration || 1,\r
8224                     callback: function() {\r
8225                         proxy.remove();\r
8226                         --count > 0 ? queue() : fly(dom).afterFx(o);\r
8227                     }\r
8228                 });\r
8229                 arguments.callee.anim = {\r
8230                     isAnimated: true,\r
8231                     stop: function(){\r
8232                         active.stop();\r
8233                     }\r
8234                 };\r
8235             };\r
8236             queue();\r
8237         });\r
8238         return me;\r
8239     },\r
8240 \r
8241    /**\r
8242     * Creates a pause before any subsequent queued effects begin.  If there are\r
8243     * no effects queued after the pause it will have no effect.\r
8244     * Usage:\r
8245 <pre><code>\r
8246 el.pause(1);\r
8247 </code></pre>\r
8248     * @param {Number} seconds The length of time to pause (in seconds)\r
8249     * @return {Ext.Element} The Element\r
8250     */\r
8251     pause : function(seconds){        \r
8252         var dom = this.dom,\r
8253             t;\r
8254 \r
8255         this.queueFx({}, function(){\r
8256             t = setTimeout(function(){\r
8257                 fly(dom).afterFx({});\r
8258             }, seconds * 1000);\r
8259             arguments.callee.anim = {\r
8260                 isAnimated: true,\r
8261                 stop: function(){\r
8262                     clearTimeout(t);\r
8263                     fly(dom).afterFx({});\r
8264                 }\r
8265             };\r
8266         });\r
8267         return this;\r
8268     },\r
8269 \r
8270    /**\r
8271     * Fade an element in (from transparent to opaque).  The ending opacity can be specified\r
8272     * using the <tt>{@link #endOpacity}</tt> config option.\r
8273     * Usage:\r
8274 <pre><code>\r
8275 // default: fade in from opacity 0 to 100%\r
8276 el.fadeIn();\r
8277 \r
8278 // custom: fade in from opacity 0 to 75% over 2 seconds\r
8279 el.fadeIn({ endOpacity: .75, duration: 2});\r
8280 \r
8281 // common config options shown with default values\r
8282 el.fadeIn({\r
8283     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)\r
8284     easing: 'easeOut',\r
8285     duration: .5\r
8286 });\r
8287 </code></pre>\r
8288     * @param {Object} options (optional) Object literal with any of the Fx config options\r
8289     * @return {Ext.Element} The Element\r
8290     */\r
8291     fadeIn : function(o){\r
8292         o = getObject(o);\r
8293         var me = this,\r
8294             dom = me.dom,\r
8295             to = o.endOpacity || 1;\r
8296         \r
8297         me.queueFx(o, function(){\r
8298             fly(dom).setOpacity(0);\r
8299             fly(dom).fixDisplay();\r
8300             dom.style.visibility = VISIBLE;\r
8301             arguments.callee.anim = fly(dom).fxanim({opacity:{to:to}},\r
8302                 o, NULL, .5, EASEOUT, function(){\r
8303                 if(to == 1){\r
8304                     fly(dom).clearOpacity();\r
8305                 }\r
8306                 fly(dom).afterFx(o);\r
8307             });\r
8308         });\r
8309         return me;\r
8310     },\r
8311 \r
8312    /**\r
8313     * Fade an element out (from opaque to transparent).  The ending opacity can be specified\r
8314     * using the <tt>{@link #endOpacity}</tt> config option.  Note that IE may require\r
8315     * <tt>{@link #useDisplay}:true</tt> in order to redisplay correctly.\r
8316     * Usage:\r
8317 <pre><code>\r
8318 // default: fade out from the element's current opacity to 0\r
8319 el.fadeOut();\r
8320 \r
8321 // custom: fade out from the element's current opacity to 25% over 2 seconds\r
8322 el.fadeOut({ endOpacity: .25, duration: 2});\r
8323 \r
8324 // common config options shown with default values\r
8325 el.fadeOut({\r
8326     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)\r
8327     easing: 'easeOut',\r
8328     duration: .5,\r
8329     remove: false,\r
8330     useDisplay: false\r
8331 });\r
8332 </code></pre>\r
8333     * @param {Object} options (optional) Object literal with any of the Fx config options\r
8334     * @return {Ext.Element} The Element\r
8335     */\r
8336     fadeOut : function(o){\r
8337         o = getObject(o);\r
8338         var me = this,\r
8339             dom = me.dom,\r
8340             style = dom.style,\r
8341             to = o.endOpacity || 0;         \r
8342         \r
8343         me.queueFx(o, function(){  \r
8344             arguments.callee.anim = fly(dom).fxanim({ \r
8345                 opacity : {to : to}},\r
8346                 o, \r
8347                 NULL, \r
8348                 .5, \r
8349                 EASEOUT, \r
8350                 function(){\r
8351                     if(to == 0){\r
8352                         Ext.Element.data(dom, 'visibilityMode') == Ext.Element.DISPLAY || o.useDisplay ? \r
8353                             style.display = "none" :\r
8354                             style.visibility = HIDDEN;\r
8355                             \r
8356                         fly(dom).clearOpacity();\r
8357                     }\r
8358                     fly(dom).afterFx(o);\r
8359             });\r
8360         });\r
8361         return me;\r
8362     },\r
8363 \r
8364    /**\r
8365     * Animates the transition of an element's dimensions from a starting height/width\r
8366     * to an ending height/width.  This method is a convenience implementation of {@link shift}.\r
8367     * Usage:\r
8368 <pre><code>\r
8369 // change height and width to 100x100 pixels\r
8370 el.scale(100, 100);\r
8371 \r
8372 // common config options shown with default values.  The height and width will default to\r
8373 // the element&#39;s existing values if passed as null.\r
8374 el.scale(\r
8375     [element&#39;s width],\r
8376     [element&#39;s height], {\r
8377         easing: 'easeOut',\r
8378         duration: .35\r
8379     }\r
8380 );\r
8381 </code></pre>\r
8382     * @param {Number} width  The new width (pass undefined to keep the original width)\r
8383     * @param {Number} height  The new height (pass undefined to keep the original height)\r
8384     * @param {Object} options (optional) Object literal with any of the Fx config options\r
8385     * @return {Ext.Element} The Element\r
8386     */\r
8387     scale : function(w, h, o){\r
8388         this.shift(Ext.apply({}, o, {\r
8389             width: w,\r
8390             height: h\r
8391         }));\r
8392         return this;\r
8393     },\r
8394 \r
8395    /**\r
8396     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.\r
8397     * Any of these properties not specified in the config object will not be changed.  This effect \r
8398     * requires that at least one new dimension, position or opacity setting must be passed in on\r
8399     * the config object in order for the function to have any effect.\r
8400     * Usage:\r
8401 <pre><code>\r
8402 // slide the element horizontally to x position 200 while changing the height and opacity\r
8403 el.shift({ x: 200, height: 50, opacity: .8 });\r
8404 \r
8405 // common config options shown with default values.\r
8406 el.shift({\r
8407     width: [element&#39;s width],\r
8408     height: [element&#39;s height],\r
8409     x: [element&#39;s x position],\r
8410     y: [element&#39;s y position],\r
8411     opacity: [element&#39;s opacity],\r
8412     easing: 'easeOut',\r
8413     duration: .35\r
8414 });\r
8415 </code></pre>\r
8416     * @param {Object} options  Object literal with any of the Fx config options\r
8417     * @return {Ext.Element} The Element\r
8418     */\r
8419     shift : function(o){\r
8420         o = getObject(o);\r
8421         var dom = this.dom,\r
8422             a = {};\r
8423                 \r
8424         this.queueFx(o, function(){\r
8425             for (var prop in o) {\r
8426                 if (o[prop] != UNDEFINED) {                                                 \r
8427                     a[prop] = {to : o[prop]};                   \r
8428                 }\r
8429             } \r
8430             \r
8431             a.width ? a.width.to = fly(dom).adjustWidth(o.width) : a;\r
8432             a.height ? a.height.to = fly(dom).adjustWidth(o.height) : a;   \r
8433             \r
8434             if (a.x || a.y || a.xy) {\r
8435                 a.points = a.xy || \r
8436                            {to : [ a.x ? a.x.to : fly(dom).getX(),\r
8437                                    a.y ? a.y.to : fly(dom).getY()]};                  \r
8438             }\r
8439 \r
8440             arguments.callee.anim = fly(dom).fxanim(a,\r
8441                 o, \r
8442                 MOTION, \r
8443                 .35, \r
8444                 EASEOUT, \r
8445                 function(){\r
8446                     fly(dom).afterFx(o);\r
8447                 });\r
8448         });\r
8449         return this;\r
8450     },\r
8451 \r
8452     /**\r
8453      * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the \r
8454      * ending point of the effect.\r
8455      * Usage:\r
8456      *<pre><code>\r
8457 // default: slide the element downward while fading out\r
8458 el.ghost();\r
8459 \r
8460 // custom: slide the element out to the right with a 2-second duration\r
8461 el.ghost('r', { duration: 2 });\r
8462 \r
8463 // common config options shown with default values\r
8464 el.ghost('b', {\r
8465     easing: 'easeOut',\r
8466     duration: .5,\r
8467     remove: false,\r
8468     useDisplay: false\r
8469 });\r
8470 </code></pre>\r
8471      * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')\r
8472      * @param {Object} options (optional) Object literal with any of the Fx config options\r
8473      * @return {Ext.Element} The Element\r
8474      */\r
8475     ghost : function(anchor, o){\r
8476         o = getObject(o);\r
8477         var me = this,\r
8478             dom = me.dom,\r
8479             st = dom.style,\r
8480             a = {opacity: {to: 0}, points: {}},\r
8481             pt = a.points,\r
8482             r,\r
8483             w,\r
8484             h;\r
8485             \r
8486         anchor = anchor || "b";\r
8487 \r
8488         me.queueFx(o, function(){\r
8489             // restore values after effect\r
8490             r = fly(dom).getFxRestore();\r
8491             w = fly(dom).getWidth();\r
8492             h = fly(dom).getHeight();\r
8493             \r
8494             function after(){\r
8495                 o.useDisplay ? fly(dom).setDisplayed(FALSE) : fly(dom).hide();   \r
8496                 fly(dom).clearOpacity();\r
8497                 fly(dom).setPositioning(r.pos);\r
8498                 st.width = r.width;\r
8499                 st.height = r.height;\r
8500                 fly(dom).afterFx(o);\r
8501             }\r
8502                 \r
8503             pt.by = fly(dom).switchStatements(anchor.toLowerCase(), function(v1,v2){ return [v1, v2];}, {\r
8504                t  : [0, -h],\r
8505                l  : [-w, 0],\r
8506                r  : [w, 0],\r
8507                b  : [0, h],\r
8508                tl : [-w, -h],\r
8509                bl : [-w, h],\r
8510                br : [w, h],\r
8511                tr : [w, -h] \r
8512             });\r
8513                 \r
8514             arguments.callee.anim = fly(dom).fxanim(a,\r
8515                 o,\r
8516                 MOTION,\r
8517                 .5,\r
8518                 EASEOUT, after);\r
8519         });\r
8520         return me;\r
8521     },\r
8522 \r
8523     /**\r
8524      * Ensures that all effects queued after syncFx is called on the element are\r
8525      * run concurrently.  This is the opposite of {@link #sequenceFx}.\r
8526      * @return {Ext.Element} The Element\r
8527      */\r
8528     syncFx : function(){\r
8529         var me = this;\r
8530         me.fxDefaults = Ext.apply(me.fxDefaults || {}, {\r
8531             block : FALSE,\r
8532             concurrent : TRUE,\r
8533             stopFx : FALSE\r
8534         });\r
8535         return me;\r
8536     },\r
8537 \r
8538     /**\r
8539      * Ensures that all effects queued after sequenceFx is called on the element are\r
8540      * run in sequence.  This is the opposite of {@link #syncFx}.\r
8541      * @return {Ext.Element} The Element\r
8542      */\r
8543     sequenceFx : function(){\r
8544         var me = this;\r
8545         me.fxDefaults = Ext.apply(me.fxDefaults || {}, {\r
8546             block : FALSE,\r
8547             concurrent : FALSE,\r
8548             stopFx : FALSE\r
8549         });\r
8550         return me;\r
8551     },\r
8552 \r
8553     /* @private */\r
8554     nextFx : function(){        \r
8555         var ef = getQueue(this.dom.id)[0];\r
8556         if(ef){\r
8557             ef.call(this);\r
8558         }\r
8559     },\r
8560 \r
8561     /**\r
8562      * Returns true if the element has any effects actively running or queued, else returns false.\r
8563      * @return {Boolean} True if element has active effects, else false\r
8564      */\r
8565     hasActiveFx : function(){\r
8566         return getQueue(this.dom.id)[0];\r
8567     },\r
8568 \r
8569     /**\r
8570      * Stops any running effects and clears the element's internal effects queue if it contains\r
8571      * any additional effects that haven't started yet.\r
8572      * @return {Ext.Element} The Element\r
8573      */\r
8574     stopFx : function(finish){\r
8575         var me = this,\r
8576             id = me.dom.id;\r
8577         if(me.hasActiveFx()){\r
8578             var cur = getQueue(id)[0];\r
8579             if(cur && cur.anim){\r
8580                 if(cur.anim.isAnimated){\r
8581                     setQueue(id, [cur]); //clear\r
8582                     cur.anim.stop(finish !== undefined ? finish : TRUE);\r
8583                 }else{\r
8584                     setQueue(id, []);\r
8585                 }\r
8586             }\r
8587         }\r
8588         return me;\r
8589     },\r
8590 \r
8591     /* @private */\r
8592     beforeFx : function(o){\r
8593         if(this.hasActiveFx() && !o.concurrent){\r
8594            if(o.stopFx){\r
8595                this.stopFx();\r
8596                return TRUE;\r
8597            }\r
8598            return FALSE;\r
8599         }\r
8600         return TRUE;\r
8601     },\r
8602 \r
8603     /**\r
8604      * Returns true if the element is currently blocking so that no other effect can be queued\r
8605      * until this effect is finished, else returns false if blocking is not set.  This is commonly\r
8606      * used to ensure that an effect initiated by a user action runs to completion prior to the\r
8607      * same effect being restarted (e.g., firing only one effect even if the user clicks several times).\r
8608      * @return {Boolean} True if blocking, else false\r
8609      */\r
8610     hasFxBlock : function(){\r
8611         var q = getQueue(this.dom.id);\r
8612         return q && q[0] && q[0].block;\r
8613     },\r
8614 \r
8615     /* @private */\r
8616     queueFx : function(o, fn){\r
8617         var me = fly(this.dom);\r
8618         if(!me.hasFxBlock()){\r
8619             Ext.applyIf(o, me.fxDefaults);\r
8620             if(!o.concurrent){\r
8621                 var run = me.beforeFx(o);\r
8622                 fn.block = o.block;\r
8623                 getQueue(me.dom.id).push(fn);\r
8624                 if(run){\r
8625                     me.nextFx();\r
8626                 }\r
8627             }else{\r
8628                 fn.call(me);\r
8629             }\r
8630         }\r
8631         return me;\r
8632     },\r
8633 \r
8634     /* @private */\r
8635     fxWrap : function(pos, o, vis){ \r
8636         var dom = this.dom,\r
8637             wrap,\r
8638             wrapXY;\r
8639         if(!o.wrap || !(wrap = Ext.getDom(o.wrap))){            \r
8640             if(o.fixPosition){\r
8641                 wrapXY = fly(dom).getXY();\r
8642             }\r
8643             var div = document.createElement("div");\r
8644             div.style.visibility = vis;\r
8645             wrap = dom.parentNode.insertBefore(div, dom);\r
8646             fly(wrap).setPositioning(pos);\r
8647             if(fly(wrap).isStyle(POSITION, "static")){\r
8648                 fly(wrap).position("relative");\r
8649             }\r
8650             fly(dom).clearPositioning('auto');\r
8651             fly(wrap).clip();\r
8652             wrap.appendChild(dom);\r
8653             if(wrapXY){\r
8654                 fly(wrap).setXY(wrapXY);\r
8655             }\r
8656         }\r
8657         return wrap;\r
8658     },\r
8659 \r
8660     /* @private */\r
8661     fxUnwrap : function(wrap, pos, o){      \r
8662         var dom = this.dom;\r
8663         fly(dom).clearPositioning();\r
8664         fly(dom).setPositioning(pos);\r
8665         if(!o.wrap){\r
8666             var pn = fly(wrap).dom.parentNode;
8667             pn.insertBefore(dom, wrap); \r
8668             fly(wrap).remove();\r
8669         }\r
8670     },\r
8671 \r
8672     /* @private */\r
8673     getFxRestore : function(){\r
8674         var st = this.dom.style;\r
8675         return {pos: this.getPositioning(), width: st.width, height : st.height};\r
8676     },\r
8677 \r
8678     /* @private */\r
8679     afterFx : function(o){\r
8680         var dom = this.dom,\r
8681             id = dom.id;\r
8682         if(o.afterStyle){\r
8683             fly(dom).setStyle(o.afterStyle);            \r
8684         }\r
8685         if(o.afterCls){\r
8686             fly(dom).addClass(o.afterCls);\r
8687         }\r
8688         if(o.remove == TRUE){\r
8689             fly(dom).remove();\r
8690         }\r
8691         if(o.callback){\r
8692             o.callback.call(o.scope, fly(dom));\r
8693         }\r
8694         if(!o.concurrent){\r
8695             getQueue(id).shift();\r
8696             fly(dom).nextFx();\r
8697         }\r
8698     },\r
8699 \r
8700     /* @private */\r
8701     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){\r
8702         animType = animType || 'run';\r
8703         opt = opt || {};\r
8704         var anim = Ext.lib.Anim[animType](\r
8705                 this.dom, \r
8706                 args,\r
8707                 (opt.duration || defaultDur) || .35,\r
8708                 (opt.easing || defaultEase) || EASEOUT,\r
8709                 cb,            \r
8710                 this\r
8711             );\r
8712         opt.anim = anim;\r
8713         return anim;\r
8714     }\r
8715 };\r
8716 \r
8717 // backwards compat\r
8718 Ext.Fx.resize = Ext.Fx.scale;\r
8719 \r
8720 //When included, Ext.Fx is automatically applied to Element so that all basic\r
8721 //effects are available directly via the Element API\r
8722 Ext.Element.addMethods(Ext.Fx);\r
8723 })();
8724 /**\r
8725  * @class Ext.CompositeElementLite\r
8726  * <p>This class encapsulates a <i>collection</i> of DOM elements, providing methods to filter\r
8727  * members, or to perform collective actions upon the whole set.</p>\r
8728  * <p>Although they are not listed, this class supports all of the methods of {@link Ext.Element} and\r
8729  * {@link Ext.Fx}. The methods from these classes will be performed on all the elements in this collection.</p>\r
8730  * Example:<pre><code>\r
8731 var els = Ext.select("#some-el div.some-class");\r
8732 // or select directly from an existing element\r
8733 var el = Ext.get('some-el');\r
8734 el.select('div.some-class');\r
8735 \r
8736 els.setWidth(100); // all elements become 100 width\r
8737 els.hide(true); // all elements fade out and hide\r
8738 // or\r
8739 els.setWidth(100).hide(true);\r
8740 </code>\r
8741  */\r
8742 Ext.CompositeElementLite = function(els, root){\r
8743     /**\r
8744      * <p>The Array of DOM elements which this CompositeElement encapsulates. Read-only.</p>\r
8745      * <p>This will not <i>usually</i> be accessed in developers' code, but developers wishing\r
8746      * to augment the capabilities of the CompositeElementLite class may use it when adding\r
8747      * methods to the class.</p>\r
8748      * <p>For example to add the <code>nextAll</code> method to the class to <b>add</b> all\r
8749      * following siblings of selected elements, the code would be</p><code><pre>\r
8750 Ext.override(Ext.CompositeElementLite, {\r
8751     nextAll: function() {\r
8752         var els = this.elements, i, l = els.length, n, r = [], ri = -1;\r
8753 \r
8754 //      Loop through all elements in this Composite, accumulating\r
8755 //      an Array of all siblings.\r
8756         for (i = 0; i < l; i++) {\r
8757             for (n = els[i].nextSibling; n; n = n.nextSibling) {\r
8758                 r[++ri] = n;\r
8759             }\r
8760         }\r
8761 \r
8762 //      Add all found siblings to this Composite\r
8763         return this.add(r);\r
8764     }\r
8765 });</pre></code>\r
8766      * @type Array\r
8767      * @property elements\r
8768      */\r
8769     this.elements = [];\r
8770     this.add(els, root);\r
8771     this.el = new Ext.Element.Flyweight();\r
8772 };\r
8773 \r
8774 Ext.CompositeElementLite.prototype = {\r
8775     isComposite: true,    \r
8776     \r
8777     // private\r
8778     getElement : function(el){\r
8779         // Set the shared flyweight dom property to the current element\r
8780         var e = this.el;\r
8781         e.dom = el;\r
8782         e.id = el.id;\r
8783         return e;\r
8784     },\r
8785     \r
8786     // private\r
8787     transformElement : function(el){\r
8788         return Ext.getDom(el);\r
8789     },\r
8790     \r
8791     /**\r
8792      * Returns the number of elements in this Composite.\r
8793      * @return Number\r
8794      */\r
8795     getCount : function(){\r
8796         return this.elements.length;\r
8797     },    \r
8798     /**\r
8799      * Adds elements to this Composite object.\r
8800      * @param {Mixed} els Either an Array of DOM elements to add, or another Composite object who's elements should be added.\r
8801      * @return {CompositeElement} This Composite object.\r
8802      */\r
8803     add : function(els, root){\r
8804         var me = this,\r
8805             elements = me.elements;\r
8806         if(!els){\r
8807             return this;\r
8808         }\r
8809         if(Ext.isString(els)){\r
8810             els = Ext.Element.selectorFunction(els, root);\r
8811         }else if(els.isComposite){\r
8812             els = els.elements;\r
8813         }else if(!Ext.isIterable(els)){\r
8814             els = [els];\r
8815         }\r
8816         \r
8817         for(var i = 0, len = els.length; i < len; ++i){\r
8818             elements.push(me.transformElement(els[i]));\r
8819         }\r
8820         return me;\r
8821     },\r
8822     \r
8823     invoke : function(fn, args){\r
8824         var me = this,\r
8825             els = me.elements,\r
8826             len = els.length, \r
8827             e, \r
8828             i;\r
8829             \r
8830         for(i = 0; i < len; i++) {\r
8831             e = els[i];\r
8832             if(e){\r
8833                 Ext.Element.prototype[fn].apply(me.getElement(e), args);\r
8834             }\r
8835         }\r
8836         return me;\r
8837     },\r
8838     /**\r
8839      * Returns a flyweight Element of the dom element object at the specified index\r
8840      * @param {Number} index\r
8841      * @return {Ext.Element}\r
8842      */\r
8843     item : function(index){\r
8844         var me = this,\r
8845             el = me.elements[index],\r
8846             out = null;\r
8847 \r
8848         if(el){\r
8849             out = me.getElement(el);\r
8850         }\r
8851         return out;\r
8852     },\r
8853 \r
8854     // fixes scope with flyweight\r
8855     addListener : function(eventName, handler, scope, opt){\r
8856         var els = this.elements,\r
8857             len = els.length,\r
8858             i, e;\r
8859         \r
8860         for(i = 0; i<len; i++) {\r
8861             e = els[i];\r
8862             if(e) {\r
8863                 Ext.EventManager.on(e, eventName, handler, scope || e, opt);\r
8864             }\r
8865         }\r
8866         return this;\r
8867     },\r
8868     /**\r
8869      * <p>Calls the passed function for each element in this composite.</p>\r
8870      * @param {Function} fn The function to call. The function is passed the following parameters:<ul>\r
8871      * <li><b>el</b> : Element<div class="sub-desc">The current Element in the iteration.\r
8872      * <b>This is the flyweight (shared) Ext.Element instance, so if you require a\r
8873      * a reference to the dom node, use el.dom.</b></div></li>\r
8874      * <li><b>c</b> : Composite<div class="sub-desc">This Composite object.</div></li>\r
8875      * <li><b>idx</b> : Number<div class="sub-desc">The zero-based index in the iteration.</div></li>\r
8876      * </ul>\r
8877      * @param {Object} scope (optional) The scope (<i>this</i> reference) in which the function is executed. (defaults to the Element)\r
8878      * @return {CompositeElement} this\r
8879      */\r
8880     each : function(fn, scope){       \r
8881         var me = this,\r
8882             els = me.elements,\r
8883             len = els.length,\r
8884             i, e;\r
8885         \r
8886         for(i = 0; i<len; i++) {\r
8887             e = els[i];\r
8888             if(e){\r
8889                 e = this.getElement(e);\r
8890                 if(fn.call(scope || e, e, me, i) === false){\r
8891                     break;\r
8892                 }\r
8893             }\r
8894         }\r
8895         return me;\r
8896     },\r
8897     \r
8898     /**\r
8899     * Clears this Composite and adds the elements passed.\r
8900     * @param {Mixed} els Either an array of DOM elements, or another Composite from which to fill this Composite.\r
8901     * @return {CompositeElement} this\r
8902     */\r
8903     fill : function(els){\r
8904         var me = this;\r
8905         me.elements = [];\r
8906         me.add(els);\r
8907         return me;\r
8908     },\r
8909     \r
8910     /**\r
8911      * Filters this composite to only elements that match the passed selector.\r
8912      * @param {String/Function} selector A string CSS selector or a comparison function.\r
8913      * The comparison function will be called with the following arguments:<ul>\r
8914      * <li><code>el</code> : Ext.Element<div class="sub-desc">The current DOM element.</div></li>\r
8915      * <li><code>index</code> : Number<div class="sub-desc">The current index within the collection.</div></li>\r
8916      * </ul>\r
8917      * @return {CompositeElement} this\r
8918      */\r
8919     filter : function(selector){\r
8920         var els = [],\r
8921             me = this,\r
8922             elements = me.elements,\r
8923             fn = Ext.isFunction(selector) ? selector\r
8924                 : function(el){\r
8925                     return el.is(selector);\r
8926                 };\r
8927                 \r
8928         \r
8929         me.each(function(el, self, i){\r
8930             if(fn(el, i) !== false){\r
8931                 els[els.length] = me.transformElement(el);\r
8932             }\r
8933         });\r
8934         me.elements = els;\r
8935         return me;\r
8936     },\r
8937     \r
8938     /**\r
8939      * Find the index of the passed element within the composite collection.\r
8940      * @param el {Mixed} The id of an element, or an Ext.Element, or an HtmlElement to find within the composite collection.\r
8941      * @return Number The index of the passed Ext.Element in the composite collection, or -1 if not found.\r
8942      */\r
8943     indexOf : function(el){\r
8944         return this.elements.indexOf(this.transformElement(el));\r
8945     },\r
8946     \r
8947     /**\r
8948     * Replaces the specified element with the passed element.\r
8949     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite\r
8950     * to replace.\r
8951     * @param {Mixed} replacement The id of an element or the Element itself.\r
8952     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.\r
8953     * @return {CompositeElement} this\r
8954     */    \r
8955     replaceElement : function(el, replacement, domReplace){\r
8956         var index = !isNaN(el) ? el : this.indexOf(el),\r
8957             d;\r
8958         if(index > -1){\r
8959             replacement = Ext.getDom(replacement);\r
8960             if(domReplace){\r
8961                 d = this.elements[index];\r
8962                 d.parentNode.insertBefore(replacement, d);\r
8963                 Ext.removeNode(d);\r
8964             }\r
8965             this.elements.splice(index, 1, replacement);\r
8966         }\r
8967         return this;\r
8968     },\r
8969     \r
8970     /**\r
8971      * Removes all elements.\r
8972      */\r
8973     clear : function(){\r
8974         this.elements = [];\r
8975     }\r
8976 };\r
8977 \r
8978 Ext.CompositeElementLite.prototype.on = Ext.CompositeElementLite.prototype.addListener;\r
8979 \r
8980 (function(){\r
8981 var fnName,\r
8982     ElProto = Ext.Element.prototype,\r
8983     CelProto = Ext.CompositeElementLite.prototype;\r
8984     \r
8985 for(fnName in ElProto){\r
8986     if(Ext.isFunction(ElProto[fnName])){\r
8987         (function(fnName){ \r
8988             CelProto[fnName] = CelProto[fnName] || function(){\r
8989                 return this.invoke(fnName, arguments);\r
8990             };\r
8991         }).call(CelProto, fnName);\r
8992         \r
8993     }\r
8994 }\r
8995 })();\r
8996 \r
8997 if(Ext.DomQuery){\r
8998     Ext.Element.selectorFunction = Ext.DomQuery.select;\r
8999\r
9000 \r
9001 /**\r
9002  * Selects elements based on the passed CSS selector to enable {@link Ext.Element Element} methods\r
9003  * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or\r
9004  * {@link Ext.CompositeElementLite CompositeElementLite} object.\r
9005  * @param {String/Array} selector The CSS selector or an array of elements\r
9006  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root\r
9007  * @return {CompositeElementLite/CompositeElement}\r
9008  * @member Ext.Element\r
9009  * @method select\r
9010  */\r
9011 Ext.Element.select = function(selector, root){\r
9012     var els;\r
9013     if(typeof selector == "string"){\r
9014         els = Ext.Element.selectorFunction(selector, root);\r
9015     }else if(selector.length !== undefined){\r
9016         els = selector;\r
9017     }else{\r
9018         throw "Invalid selector";\r
9019     }\r
9020     return new Ext.CompositeElementLite(els);\r
9021 };\r
9022 /**\r
9023  * Selects elements based on the passed CSS selector to enable {@link Ext.Element Element} methods\r
9024  * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or\r
9025  * {@link Ext.CompositeElementLite CompositeElementLite} object.\r
9026  * @param {String/Array} selector The CSS selector or an array of elements\r
9027  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root\r
9028  * @return {CompositeElementLite/CompositeElement}\r
9029  * @member Ext\r
9030  * @method select\r
9031  */\r
9032 Ext.select = Ext.Element.select;/**\r
9033  * @class Ext.CompositeElementLite\r
9034  */\r
9035 Ext.apply(Ext.CompositeElementLite.prototype, { \r
9036         addElements : function(els, root){\r
9037         if(!els){\r
9038             return this;\r
9039         }\r
9040         if(typeof els == "string"){\r
9041             els = Ext.Element.selectorFunction(els, root);\r
9042         }\r
9043         var yels = this.elements;        \r
9044             Ext.each(els, function(e) {\r
9045                 yels.push(Ext.get(e));\r
9046         });\r
9047         return this;\r
9048     },\r
9049     \r
9050     /**\r
9051      * Returns the first Element\r
9052      * @return {Ext.Element}\r
9053      */\r
9054     first : function(){\r
9055         return this.item(0);\r
9056     },   \r
9057     \r
9058     /**\r
9059      * Returns the last Element\r
9060      * @return {Ext.Element}\r
9061      */\r
9062     last : function(){\r
9063         return this.item(this.getCount()-1);\r
9064     },\r
9065     \r
9066     /**\r
9067      * Returns true if this composite contains the passed element\r
9068      * @param el {Mixed} The id of an element, or an Ext.Element, or an HtmlElement to find within the composite collection.\r
9069      * @return Boolean\r
9070      */\r
9071     contains : function(el){\r
9072         return this.indexOf(el) != -1;\r
9073     },
9074     
9075     /**\r
9076     * Removes the specified element(s).\r
9077     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite\r
9078     * or an array of any of those.\r
9079     * @param {Boolean} removeDom (optional) True to also remove the element from the document\r
9080     * @return {CompositeElement} this\r
9081     */\r
9082     removeElement : function(keys, removeDom){\r
9083         var me = this,\r
9084                 els = this.elements,        \r
9085                 el;             \r
9086             Ext.each(keys, function(val){\r
9087                     if ((el = (els[val] || els[val = me.indexOf(val)]))) {\r
9088                         if(removeDom){\r
9089                     if(el.dom){\r
9090                         el.remove();\r
9091                     }else{\r
9092                         Ext.removeNode(el);\r
9093                     }\r
9094                 }\r
9095                         els.splice(val, 1);                     \r
9096                         }\r
9097             });\r
9098         return this;\r
9099     }    \r
9100 });
9101 /**\r
9102  * @class Ext.CompositeElement\r
9103  * @extends Ext.CompositeElementLite\r
9104  * <p>This class encapsulates a <i>collection</i> of DOM elements, providing methods to filter\r
9105  * members, or to perform collective actions upon the whole set.</p>\r
9106  * <p>Although they are not listed, this class supports all of the methods of {@link Ext.Element} and\r
9107  * {@link Ext.Fx}. The methods from these classes will be performed on all the elements in this collection.</p>\r
9108  * <p>All methods return <i>this</i> and can be chained.</p>\r
9109  * Usage:\r
9110 <pre><code>\r
9111 var els = Ext.select("#some-el div.some-class", true);\r
9112 // or select directly from an existing element\r
9113 var el = Ext.get('some-el');\r
9114 el.select('div.some-class', true);\r
9115 \r
9116 els.setWidth(100); // all elements become 100 width\r
9117 els.hide(true); // all elements fade out and hide\r
9118 // or\r
9119 els.setWidth(100).hide(true);\r
9120 </code></pre>\r
9121  */\r
9122 Ext.CompositeElement = function(els, root){\r
9123     this.elements = [];\r
9124     this.add(els, root);\r
9125 };\r
9126 \r
9127 Ext.extend(Ext.CompositeElement, Ext.CompositeElementLite, {\r
9128     \r
9129     // private\r
9130     getElement : function(el){\r
9131         // In this case just return it, since we already have a reference to it\r
9132         return el;\r
9133     },\r
9134     \r
9135     // private\r
9136     transformElement : function(el){\r
9137         return Ext.get(el);\r
9138     }\r
9139 \r
9140     /**\r
9141     * Adds elements to this composite.\r
9142     * @param {String/Array} els A string CSS selector, an array of elements or an element\r
9143     * @return {CompositeElement} this\r
9144     */\r
9145 \r
9146     /**\r
9147      * Returns the Element object at the specified index\r
9148      * @param {Number} index\r
9149      * @return {Ext.Element}\r
9150      */\r
9151 \r
9152     /**\r
9153      * Iterates each <code>element</code> in this <code>composite</code>\r
9154      * calling the supplied function using {@link Ext#each}.\r
9155      * @param {Function} fn The function to be called with each\r
9156      * <code>element</code>. If the supplied function returns <tt>false</tt>,\r
9157      * iteration stops. This function is called with the following arguments:\r
9158      * <div class="mdetail-params"><ul>\r
9159      * <li><code>element</code> : <i>Ext.Element</i><div class="sub-desc">The element at the current <code>index</code>\r
9160      * in the <code>composite</code></div></li>\r
9161      * <li><code>composite</code> : <i>Object</i> <div class="sub-desc">This composite.</div></li>\r
9162      * <li><code>index</code> : <i>Number</i> <div class="sub-desc">The current index within the <code>composite</code> </div></li>\r
9163      * </ul></div>\r
9164      * @param {Object} scope (optional) The scope (<code><this</code> reference) in which the specified function is executed.\r
9165      * Defaults to the <code>element</code> at the current <code>index</code>\r
9166      * within the composite.\r
9167      * @return {CompositeElement} this\r
9168      */\r
9169 });\r
9170 \r
9171 /**\r
9172  * Selects elements based on the passed CSS selector to enable {@link Ext.Element Element} methods\r
9173  * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or\r
9174  * {@link Ext.CompositeElementLite CompositeElementLite} object.\r
9175  * @param {String/Array} selector The CSS selector or an array of elements\r
9176  * @param {Boolean} unique (optional) true to create a unique Ext.Element for each element (defaults to a shared flyweight object)\r
9177  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root\r
9178  * @return {CompositeElementLite/CompositeElement}\r
9179  * @member Ext.Element\r
9180  * @method select\r
9181  */\r
9182 Ext.Element.select = function(selector, unique, root){\r
9183     var els;\r
9184     if(typeof selector == "string"){\r
9185         els = Ext.Element.selectorFunction(selector, root);\r
9186     }else if(selector.length !== undefined){\r
9187         els = selector;\r
9188     }else{\r
9189         throw "Invalid selector";\r
9190     }\r
9191 \r
9192     return (unique === true) ? new Ext.CompositeElement(els) : new Ext.CompositeElementLite(els);\r
9193 };\r
9194 \r
9195 /**\r
9196  * Selects elements based on the passed CSS selector to enable {@link Ext.Element Element} methods\r
9197  * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or\r
9198  * {@link Ext.CompositeElementLite CompositeElementLite} object.\r
9199  * @param {String/Array} selector The CSS selector or an array of elements\r
9200  * @param {Boolean} unique (optional) true to create a unique Ext.Element for each element (defaults to a shared flyweight object)\r
9201  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root\r
9202  * @return {CompositeElementLite/CompositeElement}\r
9203  * @member Ext.Element\r
9204  * @method select\r
9205  */\r
9206 Ext.select = Ext.Element.select;(function(){\r
9207     var BEFOREREQUEST = "beforerequest",\r
9208         REQUESTCOMPLETE = "requestcomplete",\r
9209         REQUESTEXCEPTION = "requestexception",\r
9210         UNDEFINED = undefined,\r
9211         LOAD = 'load',\r
9212         POST = 'POST',\r
9213         GET = 'GET',\r
9214         WINDOW = window;\r
9215 \r
9216     /**\r
9217      * @class Ext.data.Connection\r
9218      * @extends Ext.util.Observable\r
9219      * <p>The class encapsulates a connection to the page's originating domain, allowing requests to be made\r
9220      * either to a configured URL, or to a URL specified at request time.</p>\r
9221      * <p>Requests made by this class are asynchronous, and will return immediately. No data from\r
9222      * the server will be available to the statement immediately following the {@link #request} call.\r
9223      * To process returned data, use a\r
9224      * <a href="#request-option-success" ext:member="request-option-success" ext:cls="Ext.data.Connection">success callback</a>\r
9225      * in the request options object,\r
9226      * or an {@link #requestcomplete event listener}.</p>\r
9227      * <p><h3>File Uploads</h3><a href="#request-option-isUpload" ext:member="request-option-isUpload" ext:cls="Ext.data.Connection">File uploads</a> are not performed using normal "Ajax" techniques, that\r
9228      * is they are <b>not</b> performed using XMLHttpRequests. Instead the form is submitted in the standard\r
9229      * manner with the DOM <tt>&lt;form></tt> element temporarily modified to have its\r
9230      * <a href="http://www.w3.org/TR/REC-html40/present/frames.html#adef-target">target</a> set to refer\r
9231      * to a dynamically generated, hidden <tt>&lt;iframe></tt> which is inserted into the document\r
9232      * but removed after the return data has been gathered.</p>\r
9233      * <p>The server response is parsed by the browser to create the document for the IFRAME. If the\r
9234      * server is using JSON to send the return object, then the\r
9235      * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.17">Content-Type</a> header\r
9236      * must be set to "text/html" in order to tell the browser to insert the text unchanged into the document body.</p>\r
9237      * <p>Characters which are significant to an HTML parser must be sent as HTML entities, so encode\r
9238      * "&lt;" as "&amp;lt;", "&amp;" as "&amp;amp;" etc.</p>\r
9239      * <p>The response text is retrieved from the document, and a fake XMLHttpRequest object\r
9240      * is created containing a <tt>responseText</tt> property in order to conform to the\r
9241      * requirements of event handlers and callbacks.</p>\r
9242      * <p>Be aware that file upload packets are sent with the content type <a href="http://www.faqs.org/rfcs/rfc2388.html">multipart/form</a>\r
9243      * and some server technologies (notably JEE) may require some custom processing in order to\r
9244      * retrieve parameter names and parameter values from the packet content.</p>\r
9245      * @constructor\r
9246      * @param {Object} config a configuration object.\r
9247      */\r
9248     Ext.data.Connection = function(config){\r
9249         Ext.apply(this, config);\r
9250         this.addEvents(\r
9251             /**\r
9252              * @event beforerequest\r
9253              * Fires before a network request is made to retrieve a data object.\r
9254              * @param {Connection} conn This Connection object.\r
9255              * @param {Object} options The options config object passed to the {@link #request} method.\r
9256              */\r
9257             BEFOREREQUEST,\r
9258             /**\r
9259              * @event requestcomplete\r
9260              * Fires if the request was successfully completed.\r
9261              * @param {Connection} conn This Connection object.\r
9262              * @param {Object} response The XHR object containing the response data.\r
9263              * See <a href="http://www.w3.org/TR/XMLHttpRequest/">The XMLHttpRequest Object</a>\r
9264              * for details.\r
9265              * @param {Object} options The options config object passed to the {@link #request} method.\r
9266              */\r
9267             REQUESTCOMPLETE,\r
9268             /**\r
9269              * @event requestexception\r
9270              * Fires if an error HTTP status was returned from the server.\r
9271              * See <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html">HTTP Status Code Definitions</a>\r
9272              * for details of HTTP status codes.\r
9273              * @param {Connection} conn This Connection object.\r
9274              * @param {Object} response The XHR object containing the response data.\r
9275              * See <a href="http://www.w3.org/TR/XMLHttpRequest/">The XMLHttpRequest Object</a>\r
9276              * for details.\r
9277              * @param {Object} options The options config object passed to the {@link #request} method.\r
9278              */\r
9279             REQUESTEXCEPTION\r
9280         );\r
9281         Ext.data.Connection.superclass.constructor.call(this);\r
9282     };\r
9283 \r
9284     Ext.extend(Ext.data.Connection, Ext.util.Observable, {\r
9285         /**\r
9286          * @cfg {String} url (Optional) <p>The default URL to be used for requests to the server. Defaults to undefined.</p>\r
9287          * <p>The <code>url</code> config may be a function which <i>returns</i> the URL to use for the Ajax request. The scope\r
9288          * (<code><b>this</b></code> reference) of the function is the <code>scope</code> option passed to the {@link #request} method.</p>\r
9289          */\r
9290         /**\r
9291          * @cfg {Object} extraParams (Optional) An object containing properties which are used as\r
9292          * extra parameters to each request made by this object. (defaults to undefined)\r
9293          */\r
9294         /**\r
9295          * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added\r
9296          *  to each request made by this object. (defaults to undefined)\r
9297          */\r
9298         /**\r
9299          * @cfg {String} method (Optional) The default HTTP method to be used for requests.\r
9300          * (defaults to undefined; if not set, but {@link #request} params are present, POST will be used;\r
9301          * otherwise, GET will be used.)\r
9302          */\r
9303         /**\r
9304          * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)\r
9305          */\r
9306         timeout : 30000,\r
9307         /**\r
9308          * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)\r
9309          * @type Boolean\r
9310          */\r
9311         autoAbort:false,\r
9312 \r
9313         /**\r
9314          * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)\r
9315          * @type Boolean\r
9316          */\r
9317         disableCaching: true,\r
9318 \r
9319         /**\r
9320          * @cfg {String} disableCachingParam (Optional) Change the parameter which is sent went disabling caching\r
9321          * through a cache buster. Defaults to '_dc'\r
9322          * @type String\r
9323          */\r
9324         disableCachingParam: '_dc',\r
9325 \r
9326         /**\r
9327          * <p>Sends an HTTP request to a remote server.</p>\r
9328          * <p><b>Important:</b> Ajax server requests are asynchronous, and this call will\r
9329          * return before the response has been received. Process any returned data\r
9330          * in a callback function.</p>\r
9331          * <pre><code>\r
9332 Ext.Ajax.request({\r
9333    url: 'ajax_demo/sample.json',\r
9334    success: function(response, opts) {\r
9335       var obj = Ext.decode(response.responseText);\r
9336       console.dir(obj);\r
9337    },\r
9338    failure: function(response, opts) {\r
9339       console.log('server-side failure with status code ' + response.status);\r
9340    }\r
9341 });\r
9342          * </code></pre>\r
9343          * <p>To execute a callback function in the correct scope, use the <tt>scope</tt> option.</p>\r
9344          * @param {Object} options An object which may contain the following properties:<ul>\r
9345          * <li><b>url</b> : String/Function (Optional)<div class="sub-desc">The URL to\r
9346          * which to send the request, or a function to call which returns a URL string. The scope of the\r
9347          * function is specified by the <tt>scope</tt> option. Defaults to the configured\r
9348          * <tt>{@link #url}</tt>.</div></li>\r
9349          * <li><b>params</b> : Object/String/Function (Optional)<div class="sub-desc">\r
9350          * An object containing properties which are used as parameters to the\r
9351          * request, a url encoded string or a function to call to get either. The scope of the function\r
9352          * is specified by the <tt>scope</tt> option.</div></li>\r
9353          * <li><b>method</b> : String (Optional)<div class="sub-desc">The HTTP method to use\r
9354          * for the request. Defaults to the configured method, or if no method was configured,\r
9355          * "GET" if no parameters are being sent, and "POST" if parameters are being sent.  Note that\r
9356          * the method name is case-sensitive and should be all caps.</div></li>\r
9357          * <li><b>callback</b> : Function (Optional)<div class="sub-desc">The\r
9358          * function to be called upon receipt of the HTTP response. The callback is\r
9359          * called regardless of success or failure and is passed the following\r
9360          * parameters:<ul>\r
9361          * <li><b>options</b> : Object<div class="sub-desc">The parameter to the request call.</div></li>\r
9362          * <li><b>success</b> : Boolean<div class="sub-desc">True if the request succeeded.</div></li>\r
9363          * <li><b>response</b> : Object<div class="sub-desc">The XMLHttpRequest object containing the response data.\r
9364          * See <a href="http://www.w3.org/TR/XMLHttpRequest/">http://www.w3.org/TR/XMLHttpRequest/</a> for details about\r
9365          * accessing elements of the response.</div></li>\r
9366          * </ul></div></li>\r
9367          * <li><a id="request-option-success"></a><b>success</b> : Function (Optional)<div class="sub-desc">The function\r
9368          * to be called upon success of the request. The callback is passed the following\r
9369          * parameters:<ul>\r
9370          * <li><b>response</b> : Object<div class="sub-desc">The XMLHttpRequest object containing the response data.</div></li>\r
9371          * <li><b>options</b> : Object<div class="sub-desc">The parameter to the request call.</div></li>\r
9372          * </ul></div></li>\r
9373          * <li><b>failure</b> : Function (Optional)<div class="sub-desc">The function\r
9374          * to be called upon failure of the request. The callback is passed the\r
9375          * following parameters:<ul>\r
9376          * <li><b>response</b> : Object<div class="sub-desc">The XMLHttpRequest object containing the response data.</div></li>\r
9377          * <li><b>options</b> : Object<div class="sub-desc">The parameter to the request call.</div></li>\r
9378          * </ul></div></li>\r
9379          * <li><b>scope</b> : Object (Optional)<div class="sub-desc">The scope in\r
9380          * which to execute the callbacks: The "this" object for the callback function. If the <tt>url</tt>, or <tt>params</tt> options were\r
9381          * specified as functions from which to draw values, then this also serves as the scope for those function calls.\r
9382          * Defaults to the browser window.</div></li>\r
9383          * <li><b>timeout</b> : Number (Optional)<div class="sub-desc">The timeout in milliseconds to be used for this request. Defaults to 30 seconds.</div></li>\r
9384          * <li><b>form</b> : Element/HTMLElement/String (Optional)<div class="sub-desc">The <tt>&lt;form&gt;</tt>\r
9385          * Element or the id of the <tt>&lt;form&gt;</tt> to pull parameters from.</div></li>\r
9386          * <li><a id="request-option-isUpload"></a><b>isUpload</b> : Boolean (Optional)<div class="sub-desc"><b>Only meaningful when used\r
9387          * with the <tt>form</tt> option</b>.\r
9388          * <p>True if the form object is a file upload (will be set automatically if the form was\r
9389          * configured with <b><tt>enctype</tt></b> "multipart/form-data").</p>\r
9390          * <p>File uploads are not performed using normal "Ajax" techniques, that is they are <b>not</b>\r
9391          * performed using XMLHttpRequests. Instead the form is submitted in the standard manner with the\r
9392          * DOM <tt>&lt;form></tt> element temporarily modified to have its\r
9393          * <a href="http://www.w3.org/TR/REC-html40/present/frames.html#adef-target">target</a> set to refer\r
9394          * to a dynamically generated, hidden <tt>&lt;iframe></tt> which is inserted into the document\r
9395          * but removed after the return data has been gathered.</p>\r
9396          * <p>The server response is parsed by the browser to create the document for the IFRAME. If the\r
9397          * server is using JSON to send the return object, then the\r
9398          * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.17">Content-Type</a> header\r
9399          * must be set to "text/html" in order to tell the browser to insert the text unchanged into the document body.</p>\r
9400          * <p>The response text is retrieved from the document, and a fake XMLHttpRequest object\r
9401          * is created containing a <tt>responseText</tt> property in order to conform to the\r
9402          * requirements of event handlers and callbacks.</p>\r
9403          * <p>Be aware that file upload packets are sent with the content type <a href="http://www.faqs.org/rfcs/rfc2388.html">multipart/form</a>\r
9404          * and some server technologies (notably JEE) may require some custom processing in order to\r
9405          * retrieve parameter names and parameter values from the packet content.</p>\r
9406          * </div></li>\r
9407          * <li><b>headers</b> : Object (Optional)<div class="sub-desc">Request\r
9408          * headers to set for the request.</div></li>\r
9409          * <li><b>xmlData</b> : Object (Optional)<div class="sub-desc">XML document\r
9410          * to use for the post. Note: This will be used instead of params for the post\r
9411          * data. Any params will be appended to the URL.</div></li>\r
9412          * <li><b>jsonData</b> : Object/String (Optional)<div class="sub-desc">JSON\r
9413          * data to use as the post. Note: This will be used instead of params for the post\r
9414          * data. Any params will be appended to the URL.</div></li>\r
9415          * <li><b>disableCaching</b> : Boolean (Optional)<div class="sub-desc">True\r
9416          * to add a unique cache-buster param to GET requests.</div></li>\r
9417          * </ul></p>\r
9418          * <p>The options object may also contain any other property which might be needed to perform\r
9419          * postprocessing in a callback because it is passed to callback functions.</p>\r
9420          * @return {Number} transactionId The id of the server transaction. This may be used\r
9421          * to cancel the request.\r
9422          */\r
9423         request : function(o){\r
9424             var me = this;\r
9425             if(me.fireEvent(BEFOREREQUEST, me, o)){\r
9426                 if (o.el) {\r
9427                     if(!Ext.isEmpty(o.indicatorText)){\r
9428                         me.indicatorText = '<div class="loading-indicator">'+o.indicatorText+"</div>";\r
9429                     }\r
9430                     if(me.indicatorText) {\r
9431                         Ext.getDom(o.el).innerHTML = me.indicatorText;\r
9432                     }\r
9433                     o.success = (Ext.isFunction(o.success) ? o.success : function(){}).createInterceptor(function(response) {\r
9434                         Ext.getDom(o.el).innerHTML = response.responseText;\r
9435                     });\r
9436                 }\r
9437 \r
9438                 var p = o.params,\r
9439                     url = o.url || me.url,\r
9440                     method,\r
9441                     cb = {success: me.handleResponse,\r
9442                           failure: me.handleFailure,\r
9443                           scope: me,\r
9444                           argument: {options: o},\r
9445                           timeout : o.timeout || me.timeout\r
9446                     },\r
9447                     form,\r
9448                     serForm;\r
9449 \r
9450 \r
9451                 if (Ext.isFunction(p)) {\r
9452                     p = p.call(o.scope||WINDOW, o);\r
9453                 }\r
9454 \r
9455                 p = Ext.urlEncode(me.extraParams, Ext.isObject(p) ? Ext.urlEncode(p) : p);\r
9456 \r
9457                 if (Ext.isFunction(url)) {\r
9458                     url = url.call(o.scope || WINDOW, o);\r
9459                 }\r
9460 \r
9461                 if((form = Ext.getDom(o.form))){\r
9462                     url = url || form.action;\r
9463                      if(o.isUpload || /multipart\/form-data/i.test(form.getAttribute("enctype"))) {\r
9464                          return me.doFormUpload.call(me, o, p, url);\r
9465                      }\r
9466                     serForm = Ext.lib.Ajax.serializeForm(form);\r
9467                     p = p ? (p + '&' + serForm) : serForm;\r
9468                 }\r
9469 \r
9470                 method = o.method || me.method || ((p || o.xmlData || o.jsonData) ? POST : GET);\r
9471 \r
9472                 if(method === GET && (me.disableCaching && o.disableCaching !== false) || o.disableCaching === true){\r
9473                     var dcp = o.disableCachingParam || me.disableCachingParam;\r
9474                     url = Ext.urlAppend(url, dcp + '=' + (new Date().getTime()));\r
9475                 }\r
9476 \r
9477                 o.headers = Ext.apply(o.headers || {}, me.defaultHeaders || {});\r
9478 \r
9479                 if(o.autoAbort === true || me.autoAbort) {\r
9480                     me.abort();\r
9481                 }\r
9482 \r
9483                 if((method == GET || o.xmlData || o.jsonData) && p){\r
9484                     url = Ext.urlAppend(url, p);\r
9485                     p = '';\r
9486                 }\r
9487                 return (me.transId = Ext.lib.Ajax.request(method, url, cb, p, o));\r
9488             }else{\r
9489                 return o.callback ? o.callback.apply(o.scope, [o,UNDEFINED,UNDEFINED]) : null;\r
9490             }\r
9491         },\r
9492 \r
9493         /**\r
9494          * Determine whether this object has a request outstanding.\r
9495          * @param {Number} transactionId (Optional) defaults to the last transaction\r
9496          * @return {Boolean} True if there is an outstanding request.\r
9497          */\r
9498         isLoading : function(transId){\r
9499             return transId ? Ext.lib.Ajax.isCallInProgress(transId) : !! this.transId;\r
9500         },\r
9501 \r
9502         /**\r
9503          * Aborts any outstanding request.\r
9504          * @param {Number} transactionId (Optional) defaults to the last transaction\r
9505          */\r
9506         abort : function(transId){\r
9507             if(transId || this.isLoading()){\r
9508                 Ext.lib.Ajax.abort(transId || this.transId);\r
9509             }\r
9510         },\r
9511 \r
9512         // private\r
9513         handleResponse : function(response){\r
9514             this.transId = false;\r
9515             var options = response.argument.options;\r
9516             response.argument = options ? options.argument : null;\r
9517             this.fireEvent(REQUESTCOMPLETE, this, response, options);\r
9518             if(options.success){\r
9519                 options.success.call(options.scope, response, options);\r
9520             }\r
9521             if(options.callback){\r
9522                 options.callback.call(options.scope, options, true, response);\r
9523             }\r
9524         },\r
9525 \r
9526         // private\r
9527         handleFailure : function(response, e){\r
9528             this.transId = false;\r
9529             var options = response.argument.options;\r
9530             response.argument = options ? options.argument : null;\r
9531             this.fireEvent(REQUESTEXCEPTION, this, response, options, e);\r
9532             if(options.failure){\r
9533                 options.failure.call(options.scope, response, options);\r
9534             }\r
9535             if(options.callback){\r
9536                 options.callback.call(options.scope, options, false, response);\r
9537             }\r
9538         },\r
9539 \r
9540         // private\r
9541         doFormUpload : function(o, ps, url){\r
9542             var id = Ext.id(),\r
9543                 doc = document,\r
9544                 frame = doc.createElement('iframe'),\r
9545                 form = Ext.getDom(o.form),\r
9546                 hiddens = [],\r
9547                 hd,\r
9548                 encoding = 'multipart/form-data',\r
9549                 buf = {\r
9550                     target: form.target,\r
9551                     method: form.method,\r
9552                     encoding: form.encoding,\r
9553                     enctype: form.enctype,\r
9554                     action: form.action\r
9555                 };\r
9556 \r
9557             Ext.fly(frame).set({\r
9558                 id: id,\r
9559                 name: id,\r
9560                 cls: 'x-hidden'\r
9561 \r
9562             });\r
9563 \r
9564             doc.body.appendChild(frame);\r
9565 \r
9566             //Reset the Frame to neutral domain\r
9567             Ext.fly(frame).set({\r
9568                src : Ext.SSL_SECURE_URL\r
9569             });\r
9570 \r
9571             // This is required so that IE doesn't pop the response up in a new window.\r
9572             if(Ext.isIE){\r
9573                document.frames[id].name = id;\r
9574             }\r
9575 \r
9576 \r
9577             Ext.fly(form).set({\r
9578                 target: id,\r
9579                 method: POST,\r
9580                 enctype: encoding,\r
9581                 encoding: encoding,\r
9582                 action: url || buf.action\r
9583             });\r
9584 \r
9585             // add dynamic params\r
9586             Ext.iterate(Ext.urlDecode(ps, false), function(k, v){\r
9587                 hd = doc.createElement('input');\r
9588                 Ext.fly(hd).set({\r
9589                     type: 'hidden',\r
9590                     value: v,\r
9591                     name: k\r
9592                 });\r
9593                 form.appendChild(hd);\r
9594                 hiddens.push(hd);\r
9595             });\r
9596 \r
9597             function cb(){\r
9598                 var me = this,\r
9599                     // bogus response object\r
9600                     r = {responseText : '',\r
9601                          responseXML : null,\r
9602                          argument : o.argument},\r
9603                     doc,\r
9604                     firstChild;\r
9605 \r
9606                 try{\r
9607                     doc = frame.contentWindow.document || frame.contentDocument || WINDOW.frames[id].document;\r
9608                     if(doc){\r
9609                         if(doc.body){\r
9610                             if(/textarea/i.test((firstChild = doc.body.firstChild || {}).tagName)){ // json response wrapped in textarea\r
9611                                 r.responseText = firstChild.value;\r
9612                             }else{\r
9613                                 r.responseText = doc.body.innerHTML;\r
9614                             }\r
9615                         }\r
9616                         //in IE the document may still have a body even if returns XML.\r
9617                         r.responseXML = doc.XMLDocument || doc;\r
9618                     }\r
9619                 }\r
9620                 catch(e) {}\r
9621 \r
9622                 Ext.EventManager.removeListener(frame, LOAD, cb, me);\r
9623 \r
9624                 me.fireEvent(REQUESTCOMPLETE, me, r, o);\r
9625 \r
9626                 function runCallback(fn, scope, args){\r
9627                     if(Ext.isFunction(fn)){\r
9628                         fn.apply(scope, args);\r
9629                     }\r
9630                 }\r
9631 \r
9632                 runCallback(o.success, o.scope, [r, o]);\r
9633                 runCallback(o.callback, o.scope, [o, true, r]);\r
9634 \r
9635                 if(!me.debugUploads){\r
9636                     setTimeout(function(){Ext.removeNode(frame);}, 100);\r
9637                 }\r
9638             }\r
9639 \r
9640             Ext.EventManager.on(frame, LOAD, cb, this);\r
9641             form.submit();\r
9642 \r
9643             Ext.fly(form).set(buf);\r
9644             Ext.each(hiddens, function(h) {\r
9645                 Ext.removeNode(h);\r
9646             });\r
9647         }\r
9648     });\r
9649 })();\r
9650 \r
9651 /**\r
9652  * @class Ext.Ajax\r
9653  * @extends Ext.data.Connection\r
9654  * <p>The global Ajax request class that provides a simple way to make Ajax requests\r
9655  * with maximum flexibility.</p>\r
9656  * <p>Since Ext.Ajax is a singleton, you can set common properties/events for it once\r
9657  * and override them at the request function level only if necessary.</p>\r
9658  * <p>Common <b>Properties</b> you may want to set are:<div class="mdetail-params"><ul>\r
9659  * <li><b><tt>{@link #method}</tt></b><p class="sub-desc"></p></li>\r
9660  * <li><b><tt>{@link #extraParams}</tt></b><p class="sub-desc"></p></li>\r
9661  * <li><b><tt>{@link #url}</tt></b><p class="sub-desc"></p></li>\r
9662  * </ul></div>\r
9663  * <pre><code>\r
9664 // Default headers to pass in every request\r
9665 Ext.Ajax.defaultHeaders = {\r
9666     'Powered-By': 'Ext'\r
9667 };\r
9668  * </code></pre>\r
9669  * </p>\r
9670  * <p>Common <b>Events</b> you may want to set are:<div class="mdetail-params"><ul>\r
9671  * <li><b><tt>{@link Ext.data.Connection#beforerequest beforerequest}</tt></b><p class="sub-desc"></p></li>\r
9672  * <li><b><tt>{@link Ext.data.Connection#requestcomplete requestcomplete}</tt></b><p class="sub-desc"></p></li>\r
9673  * <li><b><tt>{@link Ext.data.Connection#requestexception requestexception}</tt></b><p class="sub-desc"></p></li>\r
9674  * </ul></div>\r
9675  * <pre><code>\r
9676 // Example: show a spinner during all Ajax requests\r
9677 Ext.Ajax.on('beforerequest', this.showSpinner, this);\r
9678 Ext.Ajax.on('requestcomplete', this.hideSpinner, this);\r
9679 Ext.Ajax.on('requestexception', this.hideSpinner, this);\r
9680  * </code></pre>\r
9681  * </p>\r
9682  * <p>An example request:</p>\r
9683  * <pre><code>\r
9684 // Basic request\r
9685 Ext.Ajax.{@link Ext.data.Connection#request request}({\r
9686    url: 'foo.php',\r
9687    success: someFn,\r
9688    failure: otherFn,\r
9689    headers: {\r
9690        'my-header': 'foo'\r
9691    },\r
9692    params: { foo: 'bar' }\r
9693 });\r
9694 \r
9695 // Simple ajax form submission\r
9696 Ext.Ajax.{@link Ext.data.Connection#request request}({\r
9697     form: 'some-form',\r
9698     params: 'foo=bar'\r
9699 });\r
9700  * </code></pre>\r
9701  * </p>\r
9702  * @singleton\r
9703  */\r
9704 Ext.Ajax = new Ext.data.Connection({\r
9705     /**\r
9706      * @cfg {String} url @hide\r
9707      */\r
9708     /**\r
9709      * @cfg {Object} extraParams @hide\r
9710      */\r
9711     /**\r
9712      * @cfg {Object} defaultHeaders @hide\r
9713      */\r
9714     /**\r
9715      * @cfg {String} method (Optional) @hide\r
9716      */\r
9717     /**\r
9718      * @cfg {Number} timeout (Optional) @hide\r
9719      */\r
9720     /**\r
9721      * @cfg {Boolean} autoAbort (Optional) @hide\r
9722      */\r
9723 \r
9724     /**\r
9725      * @cfg {Boolean} disableCaching (Optional) @hide\r
9726      */\r
9727 \r
9728     /**\r
9729      * @property  disableCaching\r
9730      * True to add a unique cache-buster param to GET requests. (defaults to true)\r
9731      * @type Boolean\r
9732      */\r
9733     /**\r
9734      * @property  url\r
9735      * The default URL to be used for requests to the server. (defaults to undefined)\r
9736      * If the server receives all requests through one URL, setting this once is easier than\r
9737      * entering it on every request.\r
9738      * @type String\r
9739      */\r
9740     /**\r
9741      * @property  extraParams\r
9742      * An object containing properties which are used as extra parameters to each request made\r
9743      * by this object (defaults to undefined). Session information and other data that you need\r
9744      * to pass with each request are commonly put here.\r
9745      * @type Object\r
9746      */\r
9747     /**\r
9748      * @property  defaultHeaders\r
9749      * An object containing request headers which are added to each request made by this object\r
9750      * (defaults to undefined).\r
9751      * @type Object\r
9752      */\r
9753     /**\r
9754      * @property  method\r
9755      * The default HTTP method to be used for requests. Note that this is case-sensitive and\r
9756      * should be all caps (defaults to undefined; if not set but params are present will use\r
9757      * <tt>"POST"</tt>, otherwise will use <tt>"GET"</tt>.)\r
9758      * @type String\r
9759      */\r
9760     /**\r
9761      * @property  timeout\r
9762      * The timeout in milliseconds to be used for requests. (defaults to 30000)\r
9763      * @type Number\r
9764      */\r
9765 \r
9766     /**\r
9767      * @property  autoAbort\r
9768      * Whether a new request should abort any pending requests. (defaults to false)\r
9769      * @type Boolean\r
9770      */\r
9771     autoAbort : false,\r
9772 \r
9773     /**\r
9774      * Serialize the passed form into a url encoded string\r
9775      * @param {String/HTMLElement} form\r
9776      * @return {String}\r
9777      */\r
9778     serializeForm : function(form){\r
9779         return Ext.lib.Ajax.serializeForm(form);\r
9780     }\r
9781 });\r
9782 /**
9783  * @class Ext.Updater
9784  * @extends Ext.util.Observable
9785  * Provides AJAX-style update capabilities for Element objects.  Updater can be used to {@link #update}
9786  * an {@link Ext.Element} once, or you can use {@link #startAutoRefresh} to set up an auto-updating
9787  * {@link Ext.Element Element} on a specific interval.<br><br>
9788  * Usage:<br>
9789  * <pre><code>
9790  * var el = Ext.get("foo"); // Get Ext.Element object
9791  * var mgr = el.getUpdater();
9792  * mgr.update({
9793         url: "http://myserver.com/index.php",
9794         params: {
9795             param1: "foo",
9796             param2: "bar"
9797         }
9798  * });
9799  * ...
9800  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
9801  * <br>
9802  * // or directly (returns the same Updater instance)
9803  * var mgr = new Ext.Updater("myElementId");
9804  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
9805  * mgr.on("update", myFcnNeedsToKnow);
9806  * <br>
9807  * // short handed call directly from the element object
9808  * Ext.get("foo").load({
9809         url: "bar.php",
9810         scripts: true,
9811         params: "param1=foo&amp;param2=bar",
9812         text: "Loading Foo..."
9813  * });
9814  * </code></pre>
9815  * @constructor
9816  * Create new Updater directly.
9817  * @param {Mixed} el The element to update
9818  * @param {Boolean} forceNew (optional) By default the constructor checks to see if the passed element already
9819  * has an Updater and if it does it returns the same instance. This will skip that check (useful for extending this class).
9820  */
9821 Ext.UpdateManager = Ext.Updater = Ext.extend(Ext.util.Observable, 
9822 function() {
9823         var BEFOREUPDATE = "beforeupdate",
9824                 UPDATE = "update",
9825                 FAILURE = "failure";
9826                 
9827         // private
9828     function processSuccess(response){      
9829             var me = this;
9830         me.transaction = null;
9831         if (response.argument.form && response.argument.reset) {
9832             try { // put in try/catch since some older FF releases had problems with this
9833                 response.argument.form.reset();
9834             } catch(e){}
9835         }
9836         if (me.loadScripts) {
9837             me.renderer.render(me.el, response, me,
9838                updateComplete.createDelegate(me, [response]));
9839         } else {
9840             me.renderer.render(me.el, response, me);
9841             updateComplete.call(me, response);
9842         }
9843     }
9844     
9845     // private
9846     function updateComplete(response, type, success){
9847         this.fireEvent(type || UPDATE, this.el, response);
9848         if(Ext.isFunction(response.argument.callback)){
9849             response.argument.callback.call(response.argument.scope, this.el, Ext.isEmpty(success) ? true : false, response, response.argument.options);
9850         }
9851     }
9852
9853     // private
9854     function processFailure(response){              
9855         updateComplete.call(this, response, FAILURE, !!(this.transaction = null));
9856     }
9857             
9858         return {
9859             constructor: function(el, forceNew){
9860                     var me = this;
9861                 el = Ext.get(el);
9862                 if(!forceNew && el.updateManager){
9863                     return el.updateManager;
9864                 }
9865                 /**
9866                  * The Element object
9867                  * @type Ext.Element
9868                  */
9869                 me.el = el;
9870                 /**
9871                  * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
9872                  * @type String
9873                  */
9874                 me.defaultUrl = null;
9875         
9876                 me.addEvents(
9877                     /**
9878                      * @event beforeupdate
9879                      * Fired before an update is made, return false from your handler and the update is cancelled.
9880                      * @param {Ext.Element} el
9881                      * @param {String/Object/Function} url
9882                      * @param {String/Object} params
9883                      */
9884                     BEFOREUPDATE,
9885                     /**
9886                      * @event update
9887                      * Fired after successful update is made.
9888                      * @param {Ext.Element} el
9889                      * @param {Object} oResponseObject The response Object
9890                      */
9891                     UPDATE,
9892                     /**
9893                      * @event failure
9894                      * Fired on update failure.
9895                      * @param {Ext.Element} el
9896                      * @param {Object} oResponseObject The response Object
9897                      */
9898                     FAILURE
9899                 );
9900         
9901                 Ext.apply(me, Ext.Updater.defaults);
9902                 /**
9903                  * Blank page URL to use with SSL file uploads (defaults to {@link Ext.Updater.defaults#sslBlankUrl}).
9904                  * @property sslBlankUrl
9905                  * @type String
9906                  */
9907                 /**
9908                  * Whether to append unique parameter on get request to disable caching (defaults to {@link Ext.Updater.defaults#disableCaching}).
9909                  * @property disableCaching
9910                  * @type Boolean
9911                  */
9912                 /**
9913                  * Text for loading indicator (defaults to {@link Ext.Updater.defaults#indicatorText}).
9914                  * @property indicatorText
9915                  * @type String
9916                  */
9917                 /**
9918                  * Whether to show indicatorText when loading (defaults to {@link Ext.Updater.defaults#showLoadIndicator}).
9919                  * @property showLoadIndicator
9920                  * @type String
9921                  */
9922                 /**
9923                  * Timeout for requests or form posts in seconds (defaults to {@link Ext.Updater.defaults#timeout}).
9924                  * @property timeout
9925                  * @type Number
9926                  */
9927                 /**
9928                  * True to process scripts in the output (defaults to {@link Ext.Updater.defaults#loadScripts}).
9929                  * @property loadScripts
9930                  * @type Boolean
9931                  */
9932         
9933                 /**
9934                  * Transaction object of the current executing transaction, or null if there is no active transaction.
9935                  */
9936                 me.transaction = null;
9937                 /**
9938                  * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
9939                  * @type Function
9940                  */
9941                 me.refreshDelegate = me.refresh.createDelegate(me);
9942                 /**
9943                  * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
9944                  * @type Function
9945                  */
9946                 me.updateDelegate = me.update.createDelegate(me);
9947                 /**
9948                  * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
9949                  * @type Function
9950                  */
9951                 me.formUpdateDelegate = (me.formUpdate || function(){}).createDelegate(me);     
9952                 
9953                         /**
9954                          * The renderer for this Updater (defaults to {@link Ext.Updater.BasicRenderer}).
9955                          */
9956                 me.renderer = me.renderer || me.getDefaultRenderer();
9957                 
9958                 Ext.Updater.superclass.constructor.call(me);
9959             },
9960         
9961                 /**
9962              * Sets the content renderer for this Updater. See {@link Ext.Updater.BasicRenderer#render} for more details.
9963              * @param {Object} renderer The object implementing the render() method
9964              */
9965             setRenderer : function(renderer){
9966                 this.renderer = renderer;
9967             },  
9968         
9969             /**
9970              * Returns the current content renderer for this Updater. See {@link Ext.Updater.BasicRenderer#render} for more details.
9971              * @return {Object}
9972              */
9973             getRenderer : function(){
9974                return this.renderer;
9975             },
9976
9977             /**
9978              * This is an overrideable method which returns a reference to a default
9979              * renderer class if none is specified when creating the Ext.Updater.
9980              * Defaults to {@link Ext.Updater.BasicRenderer}
9981              */
9982             getDefaultRenderer: function() {
9983                 return new Ext.Updater.BasicRenderer();
9984             },
9985                 
9986             /**
9987              * Sets the default URL used for updates.
9988              * @param {String/Function} defaultUrl The url or a function to call to get the url
9989              */
9990             setDefaultUrl : function(defaultUrl){
9991                 this.defaultUrl = defaultUrl;
9992             },
9993         
9994             /**
9995              * Get the Element this Updater is bound to
9996              * @return {Ext.Element} The element
9997              */
9998             getEl : function(){
9999                 return this.el;
10000             },
10001         
10002                 /**
10003              * Performs an <b>asynchronous</b> request, updating this element with the response.
10004              * If params are specified it uses POST, otherwise it uses GET.<br><br>
10005              * <b>Note:</b> Due to the asynchronous nature of remote server requests, the Element
10006              * will not have been fully updated when the function returns. To post-process the returned
10007              * data, use the callback option, or an <b><code>update</code></b> event handler.
10008              * @param {Object} options A config object containing any of the following options:<ul>
10009              * <li>url : <b>String/Function</b><p class="sub-desc">The URL to request or a function which
10010              * <i>returns</i> the URL (defaults to the value of {@link Ext.Ajax#url} if not specified).</p></li>
10011              * <li>method : <b>String</b><p class="sub-desc">The HTTP method to
10012              * use. Defaults to POST if the <code>params</code> argument is present, otherwise GET.</p></li>
10013              * <li>params : <b>String/Object/Function</b><p class="sub-desc">The
10014              * parameters to pass to the server (defaults to none). These may be specified as a url-encoded
10015              * string, or as an object containing properties which represent parameters,
10016              * or as a function, which returns such an object.</p></li>
10017              * <li>scripts : <b>Boolean</b><p class="sub-desc">If <code>true</code>
10018              * any &lt;script&gt; tags embedded in the response text will be extracted
10019              * and executed (defaults to {@link Ext.Updater.defaults#loadScripts}). If this option is specified,
10020              * the callback will be called <i>after</i> the execution of the scripts.</p></li>
10021              * <li>callback : <b>Function</b><p class="sub-desc">A function to
10022              * be called when the response from the server arrives. The following
10023              * parameters are passed:<ul>
10024              * <li><b>el</b> : Ext.Element<p class="sub-desc">The Element being updated.</p></li>
10025              * <li><b>success</b> : Boolean<p class="sub-desc">True for success, false for failure.</p></li>
10026              * <li><b>response</b> : XMLHttpRequest<p class="sub-desc">The XMLHttpRequest which processed the update.</p></li>
10027              * <li><b>options</b> : Object<p class="sub-desc">The config object passed to the update call.</p></li></ul>
10028              * </p></li>
10029              * <li>scope : <b>Object</b><p class="sub-desc">The scope in which
10030              * to execute the callback (The callback's <code>this</code> reference.) If the
10031              * <code>params</code> argument is a function, this scope is used for that function also.</p></li>
10032              * <li>discardUrl : <b>Boolean</b><p class="sub-desc">By default, the URL of this request becomes
10033              * the default URL for this Updater object, and will be subsequently used in {@link #refresh}
10034              * calls.  To bypass this behavior, pass <code>discardUrl:true</code> (defaults to false).</p></li>
10035              * <li>timeout : <b>Number</b><p class="sub-desc">The number of seconds to wait for a response before
10036              * timing out (defaults to {@link Ext.Updater.defaults#timeout}).</p></li>
10037              * <li>text : <b>String</b><p class="sub-desc">The text to use as the innerHTML of the
10038              * {@link Ext.Updater.defaults#indicatorText} div (defaults to 'Loading...').  To replace the entire div, not
10039              * just the text, override {@link Ext.Updater.defaults#indicatorText} directly.</p></li>
10040              * <li>nocache : <b>Boolean</b><p class="sub-desc">Only needed for GET
10041              * requests, this option causes an extra, auto-generated parameter to be appended to the request
10042              * to defeat caching (defaults to {@link Ext.Updater.defaults#disableCaching}).</p></li></ul>
10043              * <p>
10044              * For example:
10045         <pre><code>
10046         um.update({
10047             url: "your-url.php",
10048             params: {param1: "foo", param2: "bar"}, // or a URL encoded string
10049             callback: yourFunction,
10050             scope: yourObject, //(optional scope)
10051             discardUrl: true,
10052             nocache: true,
10053             text: "Loading...",
10054             timeout: 60,
10055             scripts: false // Save time by avoiding RegExp execution.
10056         });
10057         </code></pre>
10058              */
10059             update : function(url, params, callback, discardUrl){
10060                     var me = this,
10061                         cfg, 
10062                         callerScope;
10063                         
10064                 if(me.fireEvent(BEFOREUPDATE, me.el, url, params) !== false){               
10065                     if(Ext.isObject(url)){ // must be config object
10066                         cfg = url;
10067                         url = cfg.url;
10068                         params = params || cfg.params;
10069                         callback = callback || cfg.callback;
10070                         discardUrl = discardUrl || cfg.discardUrl;
10071                         callerScope = cfg.scope;                        
10072                         if(!Ext.isEmpty(cfg.nocache)){me.disableCaching = cfg.nocache;};
10073                         if(!Ext.isEmpty(cfg.text)){me.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
10074                         if(!Ext.isEmpty(cfg.scripts)){me.loadScripts = cfg.scripts;};
10075                         if(!Ext.isEmpty(cfg.timeout)){me.timeout = cfg.timeout;};
10076                     }
10077                     me.showLoading();
10078         
10079                     if(!discardUrl){
10080                         me.defaultUrl = url;
10081                     }
10082                     if(Ext.isFunction(url)){
10083                         url = url.call(me);
10084                     }
10085         
10086                     var o = Ext.apply({}, {
10087                         url : url,
10088                         params: (Ext.isFunction(params) && callerScope) ? params.createDelegate(callerScope) : params,
10089                         success: processSuccess,
10090                         failure: processFailure,
10091                         scope: me,
10092                         callback: undefined,
10093                         timeout: (me.timeout*1000),
10094                         disableCaching: me.disableCaching,
10095                         argument: {
10096                             "options": cfg,
10097                             "url": url,
10098                             "form": null,
10099                             "callback": callback,
10100                             "scope": callerScope || window,
10101                             "params": params
10102                         }
10103                     }, cfg);
10104         
10105                     me.transaction = Ext.Ajax.request(o);
10106                 }
10107             },          
10108
10109                 /**
10110              * <p>Performs an asynchronous form post, updating this element with the response. If the form has the attribute
10111              * enctype="<a href="http://www.faqs.org/rfcs/rfc2388.html">multipart/form-data</a>", it assumes it's a file upload.
10112              * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.</p>
10113              * <p>File uploads are not performed using normal "Ajax" techniques, that is they are <b>not</b>
10114              * performed using XMLHttpRequests. Instead the form is submitted in the standard manner with the
10115              * DOM <code>&lt;form></code> element temporarily modified to have its
10116              * <a href="http://www.w3.org/TR/REC-html40/present/frames.html#adef-target">target</a> set to refer
10117              * to a dynamically generated, hidden <code>&lt;iframe></code> which is inserted into the document
10118              * but removed after the return data has been gathered.</p>
10119              * <p>Be aware that file upload packets, sent with the content type <a href="http://www.faqs.org/rfcs/rfc2388.html">multipart/form-data</a>
10120              * and some server technologies (notably JEE) may require some custom processing in order to
10121              * retrieve parameter names and parameter values from the packet content.</p>
10122              * @param {String/HTMLElement} form The form Id or form element
10123              * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
10124              * @param {Boolean} reset (optional) Whether to try to reset the form after the update
10125              * @param {Function} callback (optional) Callback when transaction is complete. The following
10126              * parameters are passed:<ul>
10127              * <li><b>el</b> : Ext.Element<p class="sub-desc">The Element being updated.</p></li>
10128              * <li><b>success</b> : Boolean<p class="sub-desc">True for success, false for failure.</p></li>
10129              * <li><b>response</b> : XMLHttpRequest<p class="sub-desc">The XMLHttpRequest which processed the update.</p></li></ul>
10130              */
10131             formUpdate : function(form, url, reset, callback){
10132                     var me = this;
10133                 if(me.fireEvent(BEFOREUPDATE, me.el, form, url) !== false){
10134                     if(Ext.isFunction(url)){
10135                         url = url.call(me);
10136                     }
10137                     form = Ext.getDom(form)
10138                     me.transaction = Ext.Ajax.request({
10139                         form: form,
10140                         url:url,
10141                         success: processSuccess,
10142                         failure: processFailure,
10143                         scope: me,
10144                         timeout: (me.timeout*1000),
10145                         argument: {
10146                             "url": url,
10147                             "form": form,
10148                             "callback": callback,
10149                             "reset": reset
10150                         }
10151                     });
10152                     me.showLoading.defer(1, me);
10153                 }
10154             },
10155                         
10156             /**
10157              * Set this element to auto refresh.  Can be canceled by calling {@link #stopAutoRefresh}.
10158              * @param {Number} interval How often to update (in seconds).
10159              * @param {String/Object/Function} url (optional) The url for this request, a config object in the same format
10160              * supported by {@link #load}, or a function to call to get the url (defaults to the last used url).  Note that while
10161              * the url used in a load call can be reused by this method, other load config options will not be reused and must be
10162              * sepcified as part of a config object passed as this paramter if needed.
10163              * @param {String/Object} params (optional) The parameters to pass as either a url encoded string
10164              * "&param1=1&param2=2" or as an object {param1: 1, param2: 2}
10165              * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
10166              * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
10167              */
10168             startAutoRefresh : function(interval, url, params, callback, refreshNow){
10169                     var me = this;
10170                 if(refreshNow){
10171                     me.update(url || me.defaultUrl, params, callback, true);
10172                 }
10173                 if(me.autoRefreshProcId){
10174                     clearInterval(me.autoRefreshProcId);
10175                 }
10176                 me.autoRefreshProcId = setInterval(me.update.createDelegate(me, [url || me.defaultUrl, params, callback, true]), interval * 1000);
10177             },
10178         
10179             /**
10180              * Stop auto refresh on this element.
10181              */
10182             stopAutoRefresh : function(){
10183                 if(this.autoRefreshProcId){
10184                     clearInterval(this.autoRefreshProcId);
10185                     delete this.autoRefreshProcId;
10186                 }
10187             },
10188         
10189             /**
10190              * Returns true if the Updater is currently set to auto refresh its content (see {@link #startAutoRefresh}), otherwise false.
10191              */
10192             isAutoRefreshing : function(){
10193                return !!this.autoRefreshProcId;
10194             },
10195         
10196             /**
10197              * Display the element's "loading" state. By default, the element is updated with {@link #indicatorText}. This
10198              * method may be overridden to perform a custom action while this Updater is actively updating its contents.
10199              */
10200             showLoading : function(){
10201                 if(this.showLoadIndicator){
10202                 this.el.dom.innerHTML = this.indicatorText;
10203                 }
10204             },
10205         
10206             /**
10207              * Aborts the currently executing transaction, if any.
10208              */
10209             abort : function(){
10210                 if(this.transaction){
10211                     Ext.Ajax.abort(this.transaction);
10212                 }
10213             },
10214         
10215             /**
10216              * Returns true if an update is in progress, otherwise false.
10217              * @return {Boolean}
10218              */
10219             isUpdating : function(){        
10220                 return this.transaction ? Ext.Ajax.isLoading(this.transaction) : false;        
10221             },
10222             
10223             /**
10224              * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
10225              * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
10226              */
10227             refresh : function(callback){
10228                 if(this.defaultUrl){
10229                         this.update(this.defaultUrl, null, callback, true);
10230                 }
10231             }
10232     }
10233 }());
10234
10235 /**
10236  * @class Ext.Updater.defaults
10237  * The defaults collection enables customizing the default properties of Updater
10238  */
10239 Ext.Updater.defaults = {
10240    /**
10241      * Timeout for requests or form posts in seconds (defaults to 30 seconds).
10242      * @type Number
10243      */
10244     timeout : 30,    
10245     /**
10246      * True to append a unique parameter to GET requests to disable caching (defaults to false).
10247      * @type Boolean
10248      */
10249     disableCaching : false,
10250     /**
10251      * Whether or not to show {@link #indicatorText} during loading (defaults to true).
10252      * @type Boolean
10253      */
10254     showLoadIndicator : true,
10255     /**
10256      * Text for loading indicator (defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
10257      * @type String
10258      */
10259     indicatorText : '<div class="loading-indicator">Loading...</div>',
10260      /**
10261      * True to process scripts by default (defaults to false).
10262      * @type Boolean
10263      */
10264     loadScripts : false,
10265     /**
10266     * Blank page URL to use with SSL file uploads (defaults to {@link Ext#SSL_SECURE_URL} if set, or "javascript:false").
10267     * @type String
10268     */
10269     sslBlankUrl : Ext.SSL_SECURE_URL      
10270 };
10271
10272
10273 /**
10274  * Static convenience method. <b>This method is deprecated in favor of el.load({url:'foo.php', ...})</b>.
10275  * Usage:
10276  * <pre><code>Ext.Updater.updateElement("my-div", "stuff.php");</code></pre>
10277  * @param {Mixed} el The element to update
10278  * @param {String} url The url
10279  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
10280  * @param {Object} options (optional) A config object with any of the Updater properties you want to set - for
10281  * example: {disableCaching:true, indicatorText: "Loading data..."}
10282  * @static
10283  * @deprecated
10284  * @member Ext.Updater
10285  */
10286 Ext.Updater.updateElement = function(el, url, params, options){
10287     var um = Ext.get(el).getUpdater();
10288     Ext.apply(um, options);
10289     um.update(url, params, options ? options.callback : null);
10290 };
10291
10292 /**
10293  * @class Ext.Updater.BasicRenderer
10294  * <p>This class is a base class implementing a simple render method which updates an element using results from an Ajax request.</p>
10295  * <p>The BasicRenderer updates the element's innerHTML with the responseText. To perform a custom render (i.e. XML or JSON processing),
10296  * create an object with a conforming {@link #render} method and pass it to setRenderer on the Updater.</p>
10297  */
10298 Ext.Updater.BasicRenderer = function(){};
10299
10300 Ext.Updater.BasicRenderer.prototype = {
10301     /**
10302      * This method is called when an Ajax response is received, and an Element needs updating.
10303      * @param {Ext.Element} el The element being rendered
10304      * @param {Object} xhr The XMLHttpRequest object
10305      * @param {Updater} updateManager The calling update manager
10306      * @param {Function} callback A callback that will need to be called if loadScripts is true on the Updater
10307      */
10308      render : function(el, response, updateManager, callback){       
10309         el.update(response.responseText, updateManager.loadScripts, callback);
10310     }
10311 };/**
10312  * @class Date
10313  *
10314  * The date parsing and formatting syntax contains a subset of
10315  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
10316  * supported will provide results equivalent to their PHP versions.
10317  *
10318  * The following is a list of all currently supported formats:
10319  * <pre>
10320 Format  Description                                                               Example returned values
10321 ------  -----------------------------------------------------------------------   -----------------------
10322   d     Day of the month, 2 digits with leading zeros                             01 to 31
10323   D     A short textual representation of the day of the week                     Mon to Sun
10324   j     Day of the month without leading zeros                                    1 to 31
10325   l     A full textual representation of the day of the week                      Sunday to Saturday
10326   N     ISO-8601 numeric representation of the day of the week                    1 (for Monday) through 7 (for Sunday)
10327   S     English ordinal suffix for the day of the month, 2 characters             st, nd, rd or th. Works well with j
10328   w     Numeric representation of the day of the week                             0 (for Sunday) to 6 (for Saturday)
10329   z     The day of the year (starting from 0)                                     0 to 364 (365 in leap years)
10330   W     ISO-8601 week number of year, weeks starting on Monday                    01 to 53
10331   F     A full textual representation of a month, such as January or March        January to December
10332   m     Numeric representation of a month, with leading zeros                     01 to 12
10333   M     A short textual representation of a month                                 Jan to Dec
10334   n     Numeric representation of a month, without leading zeros                  1 to 12
10335   t     Number of days in the given month                                         28 to 31
10336   L     Whether it's a leap year                                                  1 if it is a leap year, 0 otherwise.
10337   o     ISO-8601 year number (identical to (Y), but if the ISO week number (W)    Examples: 1998 or 2004
10338         belongs to the previous or next year, that year is used instead)
10339   Y     A full numeric representation of a year, 4 digits                         Examples: 1999 or 2003
10340   y     A two digit representation of a year                                      Examples: 99 or 03
10341   a     Lowercase Ante meridiem and Post meridiem                                 am or pm
10342   A     Uppercase Ante meridiem and Post meridiem                                 AM or PM
10343   g     12-hour format of an hour without leading zeros                           1 to 12
10344   G     24-hour format of an hour without leading zeros                           0 to 23
10345   h     12-hour format of an hour with leading zeros                              01 to 12
10346   H     24-hour format of an hour with leading zeros                              00 to 23
10347   i     Minutes, with leading zeros                                               00 to 59
10348   s     Seconds, with leading zeros                                               00 to 59
10349   u     Decimal fraction of a second                                              Examples:
10350         (minimum 1 digit, arbitrary number of digits allowed)                     001 (i.e. 0.001s) or
10351                                                                                   100 (i.e. 0.100s) or
10352                                                                                   999 (i.e. 0.999s) or
10353                                                                                   999876543210 (i.e. 0.999876543210s)
10354   O     Difference to Greenwich time (GMT) in hours and minutes                   Example: +1030
10355   P     Difference to Greenwich time (GMT) with colon between hours and minutes   Example: -08:00
10356   T     Timezone abbreviation of the machine running the code                     Examples: EST, MDT, PDT ...
10357   Z     Timezone offset in seconds (negative if west of UTC, positive if east)    -43200 to 50400
10358   c     ISO 8601 date
10359         Notes:                                                                    Examples:
10360         1) If unspecified, the month / day defaults to the current month / day,   1991 or
10361            the time defaults to midnight, while the timezone defaults to the      1992-10 or
10362            browser's timezone. If a time is specified, it must include both hours 1993-09-20 or
10363            and minutes. The "T" delimiter, seconds, milliseconds and timezone     1994-08-19T16:20+01:00 or
10364            are optional.                                                          1995-07-18T17:21:28-02:00 or
10365         2) The decimal fraction of a second, if specified, must contain at        1996-06-17T18:22:29.98765+03:00 or
10366            least 1 digit (there is no limit to the maximum number                 1997-05-16T19:23:30,12345-0400 or
10367            of digits allowed), and may be delimited by either a '.' or a ','      1998-04-15T20:24:31.2468Z or
10368         Refer to the examples on the right for the various levels of              1999-03-14T20:24:32Z or
10369         date-time granularity which are supported, or see                         2000-02-13T21:25:33
10370         http://www.w3.org/TR/NOTE-datetime for more info.                         2001-01-12 22:26:34
10371   U     Seconds since the Unix Epoch (January 1 1970 00:00:00 GMT)                1193432466 or -2138434463
10372   M$    Microsoft AJAX serialized dates                                           \/Date(1238606590509)\/ (i.e. UTC milliseconds since epoch) or
10373                                                                                   \/Date(1238606590509+0800)\/
10374 </pre>
10375  *
10376  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
10377  * <pre><code>
10378 // Sample date:
10379 // 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
10380
10381 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
10382 document.write(dt.format('Y-m-d'));                           // 2007-01-10
10383 document.write(dt.format('F j, Y, g:i a'));                   // January 10, 2007, 3:05 pm
10384 document.write(dt.format('l, \\t\\he jS \\of F Y h:i:s A'));  // Wednesday, the 10th of January 2007 03:05:01 PM
10385 </code></pre>
10386  *
10387  * Here are some standard date/time patterns that you might find helpful.  They
10388  * are not part of the source of Date.js, but to use them you can simply copy this
10389  * block of code into any script that is included after Date.js and they will also become
10390  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
10391  * <pre><code>
10392 Date.patterns = {
10393     ISO8601Long:"Y-m-d H:i:s",
10394     ISO8601Short:"Y-m-d",
10395     ShortDate: "n/j/Y",
10396     LongDate: "l, F d, Y",
10397     FullDateTime: "l, F d, Y g:i:s A",
10398     MonthDay: "F d",
10399     ShortTime: "g:i A",
10400     LongTime: "g:i:s A",
10401     SortableDateTime: "Y-m-d\\TH:i:s",
10402     UniversalSortableDateTime: "Y-m-d H:i:sO",
10403     YearMonth: "F, Y"
10404 };
10405 </code></pre>
10406  *
10407  * Example usage:
10408  * <pre><code>
10409 var dt = new Date();
10410 document.write(dt.format(Date.patterns.ShortDate));
10411 </code></pre>
10412  * <p>Developer-written, custom formats may be used by supplying both a formatting and a parsing function
10413  * which perform to specialized requirements. The functions are stored in {@link #parseFunctions} and {@link #formatFunctions}.</p>
10414  */
10415
10416 /*
10417  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
10418  * (see http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/)
10419  * They generate precompiled functions from format patterns instead of parsing and
10420  * processing each pattern every time a date is formatted. These functions are available
10421  * on every Date object.
10422  */
10423
10424 (function() {
10425
10426 /**
10427  * Global flag which determines if strict date parsing should be used.
10428  * Strict date parsing will not roll-over invalid dates, which is the
10429  * default behaviour of javascript Date objects.
10430  * (see {@link #parseDate} for more information)
10431  * Defaults to <tt>false</tt>.
10432  * @static
10433  * @type Boolean
10434 */
10435 Date.useStrict = false;
10436
10437
10438 // create private copy of Ext's String.format() method
10439 // - to remove unnecessary dependency
10440 // - to resolve namespace conflict with M$-Ajax's implementation
10441 function xf(format) {
10442     var args = Array.prototype.slice.call(arguments, 1);
10443     return format.replace(/\{(\d+)\}/g, function(m, i) {
10444         return args[i];
10445     });
10446 }
10447
10448
10449 // private
10450 Date.formatCodeToRegex = function(character, currentGroup) {
10451     // Note: currentGroup - position in regex result array (see notes for Date.parseCodes below)
10452     var p = Date.parseCodes[character];
10453
10454     if (p) {
10455       p = typeof p == 'function'? p() : p;
10456       Date.parseCodes[character] = p; // reassign function result to prevent repeated execution
10457     }
10458
10459     return p? Ext.applyIf({
10460       c: p.c? xf(p.c, currentGroup || "{0}") : p.c
10461     }, p) : {
10462         g:0,
10463         c:null,
10464         s:Ext.escapeRe(character) // treat unrecognised characters as literals
10465     }
10466 }
10467
10468 // private shorthand for Date.formatCodeToRegex since we'll be using it fairly often
10469 var $f = Date.formatCodeToRegex;
10470
10471 Ext.apply(Date, {
10472     /**
10473      * <p>An object hash in which each property is a date parsing function. The property name is the
10474      * format string which that function parses.</p>
10475      * <p>This object is automatically populated with date parsing functions as
10476      * date formats are requested for Ext standard formatting strings.</p>
10477      * <p>Custom parsing functions may be inserted into this object, keyed by a name which from then on
10478      * may be used as a format string to {@link #parseDate}.<p>
10479      * <p>Example:</p><pre><code>
10480 Date.parseFunctions['x-date-format'] = myDateParser;
10481 </code></pre>
10482      * <p>A parsing function should return a Date object, and is passed the following parameters:<div class="mdetail-params"><ul>
10483      * <li><code>date</code> : String<div class="sub-desc">The date string to parse.</div></li>
10484      * <li><code>strict</code> : Boolean<div class="sub-desc">True to validate date strings while parsing
10485      * (i.e. prevent javascript Date "rollover") (The default must be false).
10486      * Invalid date strings should return null when parsed.</div></li>
10487      * </ul></div></p>
10488      * <p>To enable Dates to also be <i>formatted</i> according to that format, a corresponding
10489      * formatting function must be placed into the {@link #formatFunctions} property.
10490      * @property parseFunctions
10491      * @static
10492      * @type Object
10493      */
10494     parseFunctions: {
10495         "M$": function(input, strict) {
10496             // note: the timezone offset is ignored since the M$ Ajax server sends
10497             // a UTC milliseconds-since-Unix-epoch value (negative values are allowed)
10498             var re = new RegExp('\\/Date\\(([-+])?(\\d+)(?:[+-]\\d{4})?\\)\\/');
10499             var r = (input || '').match(re);
10500             return r? new Date(((r[1] || '') + r[2]) * 1) : null;
10501         }
10502     },
10503     parseRegexes: [],
10504
10505     /**
10506      * <p>An object hash in which each property is a date formatting function. The property name is the
10507      * format string which corresponds to the produced formatted date string.</p>
10508      * <p>This object is automatically populated with date formatting functions as
10509      * date formats are requested for Ext standard formatting strings.</p>
10510      * <p>Custom formatting functions may be inserted into this object, keyed by a name which from then on
10511      * may be used as a format string to {@link #format}. Example:</p><pre><code>
10512 Date.formatFunctions['x-date-format'] = myDateFormatter;
10513 </code></pre>
10514      * <p>A formatting function should return a string repesentation of the passed Date object:<div class="mdetail-params"><ul>
10515      * <li><code>date</code> : Date<div class="sub-desc">The Date to format.</div></li>
10516      * </ul></div></p>
10517      * <p>To enable date strings to also be <i>parsed</i> according to that format, a corresponding
10518      * parsing function must be placed into the {@link #parseFunctions} property.
10519      * @property formatFunctions
10520      * @static
10521      * @type Object
10522      */
10523     formatFunctions: {
10524         "M$": function() {
10525             // UTC milliseconds since Unix epoch (M$-AJAX serialized date format (MRSF))
10526             return '\\/Date(' + this.getTime() + ')\\/';
10527         }
10528     },
10529
10530     y2kYear : 50,
10531
10532     /**
10533      * Date interval constant
10534      * @static
10535      * @type String
10536      */
10537     MILLI : "ms",
10538
10539     /**
10540      * Date interval constant
10541      * @static
10542      * @type String
10543      */
10544     SECOND : "s",
10545
10546     /**
10547      * Date interval constant
10548      * @static
10549      * @type String
10550      */
10551     MINUTE : "mi",
10552
10553     /** Date interval constant
10554      * @static
10555      * @type String
10556      */
10557     HOUR : "h",
10558
10559     /**
10560      * Date interval constant
10561      * @static
10562      * @type String
10563      */
10564     DAY : "d",
10565
10566     /**
10567      * Date interval constant
10568      * @static
10569      * @type String
10570      */
10571     MONTH : "mo",
10572
10573     /**
10574      * Date interval constant
10575      * @static
10576      * @type String
10577      */
10578     YEAR : "y",
10579
10580     /**
10581      * <p>An object hash containing default date values used during date parsing.</p>
10582      * <p>The following properties are available:<div class="mdetail-params"><ul>
10583      * <li><code>y</code> : Number<div class="sub-desc">The default year value. (defaults to undefined)</div></li>
10584      * <li><code>m</code> : Number<div class="sub-desc">The default 1-based month value. (defaults to undefined)</div></li>
10585      * <li><code>d</code> : Number<div class="sub-desc">The default day value. (defaults to undefined)</div></li>
10586      * <li><code>h</code> : Number<div class="sub-desc">The default hour value. (defaults to undefined)</div></li>
10587      * <li><code>i</code> : Number<div class="sub-desc">The default minute value. (defaults to undefined)</div></li>
10588      * <li><code>s</code> : Number<div class="sub-desc">The default second value. (defaults to undefined)</div></li>
10589      * <li><code>ms</code> : Number<div class="sub-desc">The default millisecond value. (defaults to undefined)</div></li>
10590      * </ul></div></p>
10591      * <p>Override these properties to customize the default date values used by the {@link #parseDate} method.</p>
10592      * <p><b>Note: In countries which experience Daylight Saving Time (i.e. DST), the <tt>h</tt>, <tt>i</tt>, <tt>s</tt>
10593      * and <tt>ms</tt> properties may coincide with the exact time in which DST takes effect.
10594      * It is the responsiblity of the developer to account for this.</b></p>
10595      * Example Usage:
10596      * <pre><code>
10597 // set default day value to the first day of the month
10598 Date.defaults.d = 1;
10599
10600 // parse a February date string containing only year and month values.
10601 // setting the default day value to 1 prevents weird date rollover issues
10602 // when attempting to parse the following date string on, for example, March 31st 2009.
10603 Date.parseDate('2009-02', 'Y-m'); // returns a Date object representing February 1st 2009
10604 </code></pre>
10605      * @property defaults
10606      * @static
10607      * @type Object
10608      */
10609     defaults: {},
10610
10611     /**
10612      * An array of textual day names.
10613      * Override these values for international dates.
10614      * Example:
10615      * <pre><code>
10616 Date.dayNames = [
10617     'SundayInYourLang',
10618     'MondayInYourLang',
10619     ...
10620 ];
10621 </code></pre>
10622      * @type Array
10623      * @static
10624      */
10625     dayNames : [
10626         "Sunday",
10627         "Monday",
10628         "Tuesday",
10629         "Wednesday",
10630         "Thursday",
10631         "Friday",
10632         "Saturday"
10633     ],
10634
10635     /**
10636      * An array of textual month names.
10637      * Override these values for international dates.
10638      * Example:
10639      * <pre><code>
10640 Date.monthNames = [
10641     'JanInYourLang',
10642     'FebInYourLang',
10643     ...
10644 ];
10645 </code></pre>
10646      * @type Array
10647      * @static
10648      */
10649     monthNames : [
10650         "January",
10651         "February",
10652         "March",
10653         "April",
10654         "May",
10655         "June",
10656         "July",
10657         "August",
10658         "September",
10659         "October",
10660         "November",
10661         "December"
10662     ],
10663
10664     /**
10665      * An object hash of zero-based javascript month numbers (with short month names as keys. note: keys are case-sensitive).
10666      * Override these values for international dates.
10667      * Example:
10668      * <pre><code>
10669 Date.monthNumbers = {
10670     'ShortJanNameInYourLang':0,
10671     'ShortFebNameInYourLang':1,
10672     ...
10673 };
10674 </code></pre>
10675      * @type Object
10676      * @static
10677      */
10678     monthNumbers : {
10679         Jan:0,
10680         Feb:1,
10681         Mar:2,
10682         Apr:3,
10683         May:4,
10684         Jun:5,
10685         Jul:6,
10686         Aug:7,
10687         Sep:8,
10688         Oct:9,
10689         Nov:10,
10690         Dec:11
10691     },
10692
10693     /**
10694      * Get the short month name for the given month number.
10695      * Override this function for international dates.
10696      * @param {Number} month A zero-based javascript month number.
10697      * @return {String} The short month name.
10698      * @static
10699      */
10700     getShortMonthName : function(month) {
10701         return Date.monthNames[month].substring(0, 3);
10702     },
10703
10704     /**
10705      * Get the short day name for the given day number.
10706      * Override this function for international dates.
10707      * @param {Number} day A zero-based javascript day number.
10708      * @return {String} The short day name.
10709      * @static
10710      */
10711     getShortDayName : function(day) {
10712         return Date.dayNames[day].substring(0, 3);
10713     },
10714
10715     /**
10716      * Get the zero-based javascript month number for the given short/full month name.
10717      * Override this function for international dates.
10718      * @param {String} name The short/full month name.
10719      * @return {Number} The zero-based javascript month number.
10720      * @static
10721      */
10722     getMonthNumber : function(name) {
10723         // handle camel casing for english month names (since the keys for the Date.monthNumbers hash are case sensitive)
10724         return Date.monthNumbers[name.substring(0, 1).toUpperCase() + name.substring(1, 3).toLowerCase()];
10725     },
10726
10727     /**
10728      * The base format-code to formatting-function hashmap used by the {@link #format} method.
10729      * Formatting functions are strings (or functions which return strings) which
10730      * will return the appropriate value when evaluated in the context of the Date object
10731      * from which the {@link #format} method is called.
10732      * Add to / override these mappings for custom date formatting.
10733      * Note: Date.format() treats characters as literals if an appropriate mapping cannot be found.
10734      * Example:
10735      * <pre><code>
10736 Date.formatCodes.x = "String.leftPad(this.getDate(), 2, '0')";
10737 (new Date()).format("X"); // returns the current day of the month
10738 </code></pre>
10739      * @type Object
10740      * @static
10741      */
10742     formatCodes : {
10743         d: "String.leftPad(this.getDate(), 2, '0')",
10744         D: "Date.getShortDayName(this.getDay())", // get localised short day name
10745         j: "this.getDate()",
10746         l: "Date.dayNames[this.getDay()]",
10747         N: "(this.getDay() ? this.getDay() : 7)",
10748         S: "this.getSuffix()",
10749         w: "this.getDay()",
10750         z: "this.getDayOfYear()",
10751         W: "String.leftPad(this.getWeekOfYear(), 2, '0')",
10752         F: "Date.monthNames[this.getMonth()]",
10753         m: "String.leftPad(this.getMonth() + 1, 2, '0')",
10754         M: "Date.getShortMonthName(this.getMonth())", // get localised short month name
10755         n: "(this.getMonth() + 1)",
10756         t: "this.getDaysInMonth()",
10757         L: "(this.isLeapYear() ? 1 : 0)",
10758         o: "(this.getFullYear() + (this.getWeekOfYear() == 1 && this.getMonth() > 0 ? +1 : (this.getWeekOfYear() >= 52 && this.getMonth() < 11 ? -1 : 0)))",
10759         Y: "this.getFullYear()",
10760         y: "('' + this.getFullYear()).substring(2, 4)",
10761         a: "(this.getHours() < 12 ? 'am' : 'pm')",
10762         A: "(this.getHours() < 12 ? 'AM' : 'PM')",
10763         g: "((this.getHours() % 12) ? this.getHours() % 12 : 12)",
10764         G: "this.getHours()",
10765         h: "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0')",
10766         H: "String.leftPad(this.getHours(), 2, '0')",
10767         i: "String.leftPad(this.getMinutes(), 2, '0')",
10768         s: "String.leftPad(this.getSeconds(), 2, '0')",
10769         u: "String.leftPad(this.getMilliseconds(), 3, '0')",
10770         O: "this.getGMTOffset()",
10771         P: "this.getGMTOffset(true)",
10772         T: "this.getTimezone()",
10773         Z: "(this.getTimezoneOffset() * -60)",
10774
10775         c: function() { // ISO-8601 -- GMT format
10776             for (var c = "Y-m-dTH:i:sP", code = [], i = 0, l = c.length; i < l; ++i) {
10777                 var e = c.charAt(i);
10778                 code.push(e == "T" ? "'T'" : Date.getFormatCode(e)); // treat T as a character literal
10779             }
10780             return code.join(" + ");
10781         },
10782         /*
10783         c: function() { // ISO-8601 -- UTC format
10784             return [
10785               "this.getUTCFullYear()", "'-'",
10786               "String.leftPad(this.getUTCMonth() + 1, 2, '0')", "'-'",
10787               "String.leftPad(this.getUTCDate(), 2, '0')",
10788               "'T'",
10789               "String.leftPad(this.getUTCHours(), 2, '0')", "':'",
10790               "String.leftPad(this.getUTCMinutes(), 2, '0')", "':'",
10791               "String.leftPad(this.getUTCSeconds(), 2, '0')",
10792               "'Z'"
10793             ].join(" + ");
10794         },
10795         */
10796
10797         U: "Math.round(this.getTime() / 1000)"
10798     },
10799
10800     /**
10801      * Checks if the passed Date parameters will cause a javascript Date "rollover".
10802      * @param {Number} year 4-digit year
10803      * @param {Number} month 1-based month-of-year
10804      * @param {Number} day Day of month
10805      * @param {Number} hour (optional) Hour
10806      * @param {Number} minute (optional) Minute
10807      * @param {Number} second (optional) Second
10808      * @param {Number} millisecond (optional) Millisecond
10809      * @return {Boolean} true if the passed parameters do not cause a Date "rollover", false otherwise.
10810      * @static
10811      */
10812     isValid : function(y, m, d, h, i, s, ms) {
10813         // setup defaults
10814         h = h || 0;
10815         i = i || 0;
10816         s = s || 0;
10817         ms = ms || 0;
10818
10819         var dt = new Date(y, m - 1, d, h, i, s, ms);
10820
10821         return y == dt.getFullYear() &&
10822             m == dt.getMonth() + 1 &&
10823             d == dt.getDate() &&
10824             h == dt.getHours() &&
10825             i == dt.getMinutes() &&
10826             s == dt.getSeconds() &&
10827             ms == dt.getMilliseconds();
10828     },
10829
10830     /**
10831      * Parses the passed string using the specified date format.
10832      * Note that this function expects normal calendar dates, meaning that months are 1-based (i.e. 1 = January).
10833      * The {@link #defaults} hash will be used for any date value (i.e. year, month, day, hour, minute, second or millisecond)
10834      * which cannot be found in the passed string. If a corresponding default date value has not been specified in the {@link #defaults} hash,
10835      * the current date's year, month, day or DST-adjusted zero-hour time value will be used instead.
10836      * Keep in mind that the input date string must precisely match the specified format string
10837      * in order for the parse operation to be successful (failed parse operations return a null value).
10838      * <p>Example:</p><pre><code>
10839 //dt = Fri May 25 2007 (current date)
10840 var dt = new Date();
10841
10842 //dt = Thu May 25 2006 (today&#39;s month/day in 2006)
10843 dt = Date.parseDate("2006", "Y");
10844
10845 //dt = Sun Jan 15 2006 (all date parts specified)
10846 dt = Date.parseDate("2006-01-15", "Y-m-d");
10847
10848 //dt = Sun Jan 15 2006 15:20:01
10849 dt = Date.parseDate("2006-01-15 3:20:01 PM", "Y-m-d g:i:s A");
10850
10851 // attempt to parse Sun Feb 29 2006 03:20:01 in strict mode
10852 dt = Date.parseDate("2006-02-29 03:20:01", "Y-m-d H:i:s", true); // returns null
10853 </code></pre>
10854      * @param {String} input The raw date string.
10855      * @param {String} format The expected date string format.
10856      * @param {Boolean} strict (optional) True to validate date strings while parsing (i.e. prevents javascript Date "rollover")
10857                         (defaults to false). Invalid date strings will return null when parsed.
10858      * @return {Date} The parsed Date.
10859      * @static
10860      */
10861     parseDate : function(input, format, strict) {
10862         var p = Date.parseFunctions;
10863         if (p[format] == null) {
10864             Date.createParser(format);
10865         }
10866         return p[format](input, Ext.isDefined(strict) ? strict : Date.useStrict);
10867     },
10868
10869     // private
10870     getFormatCode : function(character) {
10871         var f = Date.formatCodes[character];
10872
10873         if (f) {
10874           f = typeof f == 'function'? f() : f;
10875           Date.formatCodes[character] = f; // reassign function result to prevent repeated execution
10876         }
10877
10878         // note: unknown characters are treated as literals
10879         return f || ("'" + String.escape(character) + "'");
10880     },
10881
10882     // private
10883     createFormat : function(format) {
10884         var code = [],
10885             special = false,
10886             ch = '';
10887
10888         for (var i = 0; i < format.length; ++i) {
10889             ch = format.charAt(i);
10890             if (!special && ch == "\\") {
10891                 special = true;
10892             } else if (special) {
10893                 special = false;
10894                 code.push("'" + String.escape(ch) + "'");
10895             } else {
10896                 code.push(Date.getFormatCode(ch))
10897             }
10898         }
10899         Date.formatFunctions[format] = new Function("return " + code.join('+'));
10900     },
10901
10902     // private
10903     createParser : function() {
10904         var code = [
10905             "var dt, y, m, d, h, i, s, ms, o, z, zz, u, v,",
10906                 "def = Date.defaults,",
10907                 "results = String(input).match(Date.parseRegexes[{0}]);", // either null, or an array of matched strings
10908
10909             "if(results){",
10910                 "{1}",
10911
10912                 "if(u != null){", // i.e. unix time is defined
10913                     "v = new Date(u * 1000);", // give top priority to UNIX time
10914                 "}else{",
10915                     // create Date object representing midnight of the current day;
10916                     // this will provide us with our date defaults
10917                     // (note: clearTime() handles Daylight Saving Time automatically)
10918                     "dt = (new Date()).clearTime();",
10919
10920                     // date calculations (note: these calculations create a dependency on Ext.num())
10921                     "y = Ext.num(y, Ext.num(def.y, dt.getFullYear()));",
10922                     "m = Ext.num(m, Ext.num(def.m - 1, dt.getMonth()));",
10923                     "d = Ext.num(d, Ext.num(def.d, dt.getDate()));",
10924
10925                     // time calculations (note: these calculations create a dependency on Ext.num())
10926                     "h  = Ext.num(h, Ext.num(def.h, dt.getHours()));",
10927                     "i  = Ext.num(i, Ext.num(def.i, dt.getMinutes()));",
10928                     "s  = Ext.num(s, Ext.num(def.s, dt.getSeconds()));",
10929                     "ms = Ext.num(ms, Ext.num(def.ms, dt.getMilliseconds()));",
10930
10931                     "if(z >= 0 && y >= 0){",
10932                         // both the year and zero-based day of year are defined and >= 0.
10933                         // these 2 values alone provide sufficient info to create a full date object
10934
10935                         // create Date object representing January 1st for the given year
10936                         "v = new Date(y, 0, 1, h, i, s, ms);",
10937
10938                         // then add day of year, checking for Date "rollover" if necessary
10939                         "v = !strict? v : (strict === true && (z <= 364 || (v.isLeapYear() && z <= 365))? v.add(Date.DAY, z) : null);",
10940                     "}else if(strict === true && !Date.isValid(y, m + 1, d, h, i, s, ms)){", // check for Date "rollover"
10941                         "v = null;", // invalid date, so return null
10942                     "}else{",
10943                         // plain old Date object
10944                         "v = new Date(y, m, d, h, i, s, ms);",
10945                     "}",
10946                 "}",
10947             "}",
10948
10949             "if(v){",
10950                 // favour UTC offset over GMT offset
10951                 "if(zz != null){",
10952                     // reset to UTC, then add offset
10953                     "v = v.add(Date.SECOND, -v.getTimezoneOffset() * 60 - zz);",
10954                 "}else if(o){",
10955                     // reset to GMT, then add offset
10956                     "v = v.add(Date.MINUTE, -v.getTimezoneOffset() + (sn == '+'? -1 : 1) * (hr * 60 + mn));",
10957                 "}",
10958             "}",
10959
10960             "return v;"
10961         ].join('\n');
10962
10963         return function(format) {
10964             var regexNum = Date.parseRegexes.length,
10965                 currentGroup = 1,
10966                 calc = [],
10967                 regex = [],
10968                 special = false,
10969                 ch = "";
10970
10971             for (var i = 0; i < format.length; ++i) {
10972                 ch = format.charAt(i);
10973                 if (!special && ch == "\\") {
10974                     special = true;
10975                 } else if (special) {
10976                     special = false;
10977                     regex.push(String.escape(ch));
10978                 } else {
10979                     var obj = $f(ch, currentGroup);
10980                     currentGroup += obj.g;
10981                     regex.push(obj.s);
10982                     if (obj.g && obj.c) {
10983                         calc.push(obj.c);
10984                     }
10985                 }
10986             }
10987
10988             Date.parseRegexes[regexNum] = new RegExp("^" + regex.join('') + "$", "i");
10989             Date.parseFunctions[format] = new Function("input", "strict", xf(code, regexNum, calc.join('')));
10990         }
10991     }(),
10992
10993     // private
10994     parseCodes : {
10995         /*
10996          * Notes:
10997          * g = {Number} calculation group (0 or 1. only group 1 contributes to date calculations.)
10998          * c = {String} calculation method (required for group 1. null for group 0. {0} = currentGroup - position in regex result array)
10999          * s = {String} regex pattern. all matches are stored in results[], and are accessible by the calculation mapped to 'c'
11000          */
11001         d: {
11002             g:1,
11003             c:"d = parseInt(results[{0}], 10);\n",
11004             s:"(\\d{2})" // day of month with leading zeroes (01 - 31)
11005         },
11006         j: {
11007             g:1,
11008             c:"d = parseInt(results[{0}], 10);\n",
11009             s:"(\\d{1,2})" // day of month without leading zeroes (1 - 31)
11010         },
11011         D: function() {
11012             for (var a = [], i = 0; i < 7; a.push(Date.getShortDayName(i)), ++i); // get localised short day names
11013             return {
11014                 g:0,
11015                 c:null,
11016                 s:"(?:" + a.join("|") +")"
11017             }
11018         },
11019         l: function() {
11020             return {
11021                 g:0,
11022                 c:null,
11023                 s:"(?:" + Date.dayNames.join("|") + ")"
11024             }
11025         },
11026         N: {
11027             g:0,
11028             c:null,
11029             s:"[1-7]" // ISO-8601 day number (1 (monday) - 7 (sunday))
11030         },
11031         S: {
11032             g:0,
11033             c:null,
11034             s:"(?:st|nd|rd|th)"
11035         },
11036         w: {
11037             g:0,
11038             c:null,
11039             s:"[0-6]" // javascript day number (0 (sunday) - 6 (saturday))
11040         },
11041         z: {
11042             g:1,
11043             c:"z = parseInt(results[{0}], 10);\n",
11044             s:"(\\d{1,3})" // day of the year (0 - 364 (365 in leap years))
11045         },
11046         W: {
11047             g:0,
11048             c:null,
11049             s:"(?:\\d{2})" // ISO-8601 week number (with leading zero)
11050         },
11051         F: function() {
11052             return {
11053                 g:1,
11054                 c:"m = parseInt(Date.getMonthNumber(results[{0}]), 10);\n", // get localised month number
11055                 s:"(" + Date.monthNames.join("|") + ")"
11056             }
11057         },
11058         M: function() {
11059             for (var a = [], i = 0; i < 12; a.push(Date.getShortMonthName(i)), ++i); // get localised short month names
11060             return Ext.applyIf({
11061                 s:"(" + a.join("|") + ")"
11062             }, $f("F"));
11063         },
11064         m: {
11065             g:1,
11066             c:"m = parseInt(results[{0}], 10) - 1;\n",
11067             s:"(\\d{2})" // month number with leading zeros (01 - 12)
11068         },
11069         n: {
11070             g:1,
11071             c:"m = parseInt(results[{0}], 10) - 1;\n",
11072             s:"(\\d{1,2})" // month number without leading zeros (1 - 12)
11073         },
11074         t: {
11075             g:0,
11076             c:null,
11077             s:"(?:\\d{2})" // no. of days in the month (28 - 31)
11078         },
11079         L: {
11080             g:0,
11081             c:null,
11082             s:"(?:1|0)"
11083         },
11084         o: function() {
11085             return $f("Y");
11086         },
11087         Y: {
11088             g:1,
11089             c:"y = parseInt(results[{0}], 10);\n",
11090             s:"(\\d{4})" // 4-digit year
11091         },
11092         y: {
11093             g:1,
11094             c:"var ty = parseInt(results[{0}], 10);\n"
11095                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n", // 2-digit year
11096             s:"(\\d{1,2})"
11097         },
11098         a: {
11099             g:1,
11100             c:"if (results[{0}] == 'am') {\n"
11101                 + "if (!h || h == 12) { h = 0; }\n"
11102                 + "} else { if (!h || h < 12) { h = (h || 0) + 12; }}",
11103             s:"(am|pm)"
11104         },
11105         A: {
11106             g:1,
11107             c:"if (results[{0}] == 'AM') {\n"
11108                 + "if (!h || h == 12) { h = 0; }\n"
11109                 + "} else { if (!h || h < 12) { h = (h || 0) + 12; }}",
11110             s:"(AM|PM)"
11111         },
11112         g: function() {
11113             return $f("G");
11114         },
11115         G: {
11116             g:1,
11117             c:"h = parseInt(results[{0}], 10);\n",
11118             s:"(\\d{1,2})" // 24-hr format of an hour without leading zeroes (0 - 23)
11119         },
11120         h: function() {
11121             return $f("H");
11122         },
11123         H: {
11124             g:1,
11125             c:"h = parseInt(results[{0}], 10);\n",
11126             s:"(\\d{2})" //  24-hr format of an hour with leading zeroes (00 - 23)
11127         },
11128         i: {
11129             g:1,
11130             c:"i = parseInt(results[{0}], 10);\n",
11131             s:"(\\d{2})" // minutes with leading zeros (00 - 59)
11132         },
11133         s: {
11134             g:1,
11135             c:"s = parseInt(results[{0}], 10);\n",
11136             s:"(\\d{2})" // seconds with leading zeros (00 - 59)
11137         },
11138         u: {
11139             g:1,
11140             c:"ms = results[{0}]; ms = parseInt(ms, 10)/Math.pow(10, ms.length - 3);\n",
11141             s:"(\\d+)" // decimal fraction of a second (minimum = 1 digit, maximum = unlimited)
11142         },
11143         O: {
11144             g:1,
11145             c:[
11146                 "o = results[{0}];",
11147                 "var sn = o.substring(0,1),", // get + / - sign
11148                     "hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60),", // get hours (performs minutes-to-hour conversion also, just in case)
11149                     "mn = o.substring(3,5) % 60;", // get minutes
11150                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))? (sn + String.leftPad(hr, 2, '0') + String.leftPad(mn, 2, '0')) : null;\n" // -12hrs <= GMT offset <= 14hrs
11151             ].join("\n"),
11152             s: "([+\-]\\d{4})" // GMT offset in hrs and mins
11153         },
11154         P: {
11155             g:1,
11156             c:[
11157                 "o = results[{0}];",
11158                 "var sn = o.substring(0,1),", // get + / - sign
11159                     "hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60),", // get hours (performs minutes-to-hour conversion also, just in case)
11160                     "mn = o.substring(4,6) % 60;", // get minutes
11161                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))? (sn + String.leftPad(hr, 2, '0') + String.leftPad(mn, 2, '0')) : null;\n" // -12hrs <= GMT offset <= 14hrs
11162             ].join("\n"),
11163             s: "([+\-]\\d{2}:\\d{2})" // GMT offset in hrs and mins (with colon separator)
11164         },
11165         T: {
11166             g:0,
11167             c:null,
11168             s:"[A-Z]{1,4}" // timezone abbrev. may be between 1 - 4 chars
11169         },
11170         Z: {
11171             g:1,
11172             c:"zz = results[{0}] * 1;\n" // -43200 <= UTC offset <= 50400
11173                   + "zz = (-43200 <= zz && zz <= 50400)? zz : null;\n",
11174             s:"([+\-]?\\d{1,5})" // leading '+' sign is optional for UTC offset
11175         },
11176         c: function() {
11177             var calc = [],
11178                 arr = [
11179                     $f("Y", 1), // year
11180                     $f("m", 2), // month
11181                     $f("d", 3), // day
11182                     $f("h", 4), // hour
11183                     $f("i", 5), // minute
11184                     $f("s", 6), // second
11185                     {c:"ms = results[7] || '0'; ms = parseInt(ms, 10)/Math.pow(10, ms.length - 3);\n"}, // decimal fraction of a second (minimum = 1 digit, maximum = unlimited)
11186                     {c:[ // allow either "Z" (i.e. UTC) or "-0530" or "+08:00" (i.e. UTC offset) timezone delimiters. assumes local timezone if no timezone is specified
11187                         "if(results[8]) {", // timezone specified
11188                             "if(results[8] == 'Z'){",
11189                                 "zz = 0;", // UTC
11190                             "}else if (results[8].indexOf(':') > -1){",
11191                                 $f("P", 8).c, // timezone offset with colon separator
11192                             "}else{",
11193                                 $f("O", 8).c, // timezone offset without colon separator
11194                             "}",
11195                         "}"
11196                     ].join('\n')}
11197                 ];
11198
11199             for (var i = 0, l = arr.length; i < l; ++i) {
11200                 calc.push(arr[i].c);
11201             }
11202
11203             return {
11204                 g:1,
11205                 c:calc.join(""),
11206                 s:[
11207                     arr[0].s, // year (required)
11208                     "(?:", "-", arr[1].s, // month (optional)
11209                         "(?:", "-", arr[2].s, // day (optional)
11210                             "(?:",
11211                                 "(?:T| )?", // time delimiter -- either a "T" or a single blank space
11212                                 arr[3].s, ":", arr[4].s,  // hour AND minute, delimited by a single colon (optional). MUST be preceded by either a "T" or a single blank space
11213                                 "(?::", arr[5].s, ")?", // seconds (optional)
11214                                 "(?:(?:\\.|,)(\\d+))?", // decimal fraction of a second (e.g. ",12345" or ".98765") (optional)
11215                                 "(Z|(?:[-+]\\d{2}(?::)?\\d{2}))?", // "Z" (UTC) or "-0530" (UTC offset without colon delimiter) or "+08:00" (UTC offset with colon delimiter) (optional)
11216                             ")?",
11217                         ")?",
11218                     ")?"
11219                 ].join("")
11220             }
11221         },
11222         U: {
11223             g:1,
11224             c:"u = parseInt(results[{0}], 10);\n",
11225             s:"(-?\\d+)" // leading minus sign indicates seconds before UNIX epoch
11226         }
11227     }
11228 });
11229
11230 }());
11231
11232 Ext.apply(Date.prototype, {
11233     // private
11234     dateFormat : function(format) {
11235         if (Date.formatFunctions[format] == null) {
11236             Date.createFormat(format);
11237         }
11238         return Date.formatFunctions[format].call(this);
11239     },
11240
11241     /**
11242      * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
11243      *
11244      * Note: The date string returned by the javascript Date object's toString() method varies
11245      * between browsers (e.g. FF vs IE) and system region settings (e.g. IE in Asia vs IE in America).
11246      * For a given date string e.g. "Thu Oct 25 2007 22:55:35 GMT+0800 (Malay Peninsula Standard Time)",
11247      * getTimezone() first tries to get the timezone abbreviation from between a pair of parentheses
11248      * (which may or may not be present), failing which it proceeds to get the timezone abbreviation
11249      * from the GMT offset portion of the date string.
11250      * @return {String} The abbreviated timezone name (e.g. 'CST', 'PDT', 'EDT', 'MPST' ...).
11251      */
11252     getTimezone : function() {
11253         // the following list shows the differences between date strings from different browsers on a WinXP SP2 machine from an Asian locale:
11254         //
11255         // Opera  : "Thu, 25 Oct 2007 22:53:45 GMT+0800" -- shortest (weirdest) date string of the lot
11256         // Safari : "Thu Oct 25 2007 22:55:35 GMT+0800 (Malay Peninsula Standard Time)" -- value in parentheses always gives the correct timezone (same as FF)
11257         // FF     : "Thu Oct 25 2007 22:55:35 GMT+0800 (Malay Peninsula Standard Time)" -- value in parentheses always gives the correct timezone
11258         // IE     : "Thu Oct 25 22:54:35 UTC+0800 2007" -- (Asian system setting) look for 3-4 letter timezone abbrev
11259         // IE     : "Thu Oct 25 17:06:37 PDT 2007" -- (American system setting) look for 3-4 letter timezone abbrev
11260         //
11261         // this crazy regex attempts to guess the correct timezone abbreviation despite these differences.
11262         // step 1: (?:\((.*)\) -- find timezone in parentheses
11263         // step 2: ([A-Z]{1,4})(?:[\-+][0-9]{4})?(?: -?\d+)?) -- if nothing was found in step 1, find timezone from timezone offset portion of date string
11264         // step 3: remove all non uppercase characters found in step 1 and 2
11265         return this.toString().replace(/^.* (?:\((.*)\)|([A-Z]{1,4})(?:[\-+][0-9]{4})?(?: -?\d+)?)$/, "$1$2").replace(/[^A-Z]/g, "");
11266     },
11267
11268     /**
11269      * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
11270      * @param {Boolean} colon (optional) true to separate the hours and minutes with a colon (defaults to false).
11271      * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600').
11272      */
11273     getGMTOffset : function(colon) {
11274         return (this.getTimezoneOffset() > 0 ? "-" : "+")
11275             + String.leftPad(Math.floor(Math.abs(this.getTimezoneOffset()) / 60), 2, "0")
11276             + (colon ? ":" : "")
11277             + String.leftPad(Math.abs(this.getTimezoneOffset() % 60), 2, "0");
11278     },
11279
11280     /**
11281      * Get the numeric day number of the year, adjusted for leap year.
11282      * @return {Number} 0 to 364 (365 in leap years).
11283      */
11284     getDayOfYear: function() {
11285         var num = 0,
11286             d = this.clone(),
11287             m = this.getMonth(),
11288             i;
11289
11290         for (i = 0, d.setDate(1), d.setMonth(0); i < m; d.setMonth(++i)) {
11291             num += d.getDaysInMonth();
11292         }
11293         return num + this.getDate() - 1;
11294     },
11295
11296     /**
11297      * Get the numeric ISO-8601 week number of the year.
11298      * (equivalent to the format specifier 'W', but without a leading zero).
11299      * @return {Number} 1 to 53
11300      */
11301     getWeekOfYear : function() {
11302         // adapted from http://www.merlyn.demon.co.uk/weekcalc.htm
11303         var ms1d = 864e5, // milliseconds in a day
11304             ms7d = 7 * ms1d; // milliseconds in a week
11305
11306         return function() { // return a closure so constants get calculated only once
11307             var DC3 = Date.UTC(this.getFullYear(), this.getMonth(), this.getDate() + 3) / ms1d, // an Absolute Day Number
11308                 AWN = Math.floor(DC3 / 7), // an Absolute Week Number
11309                 Wyr = new Date(AWN * ms7d).getUTCFullYear();
11310
11311             return AWN - Math.floor(Date.UTC(Wyr, 0, 7) / ms7d) + 1;
11312         }
11313     }(),
11314
11315     /**
11316      * Checks if the current date falls within a leap year.
11317      * @return {Boolean} True if the current date falls within a leap year, false otherwise.
11318      */
11319     isLeapYear : function() {
11320         var year = this.getFullYear();
11321         return !!((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
11322     },
11323
11324     /**
11325      * Get the first day of the current month, adjusted for leap year.  The returned value
11326      * is the numeric day index within the week (0-6) which can be used in conjunction with
11327      * the {@link #monthNames} array to retrieve the textual day name.
11328      * Example:
11329      * <pre><code>
11330 var dt = new Date('1/10/2007');
11331 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
11332 </code></pre>
11333      * @return {Number} The day number (0-6).
11334      */
11335     getFirstDayOfMonth : function() {
11336         var day = (this.getDay() - (this.getDate() - 1)) % 7;
11337         return (day < 0) ? (day + 7) : day;
11338     },
11339
11340     /**
11341      * Get the last day of the current month, adjusted for leap year.  The returned value
11342      * is the numeric day index within the week (0-6) which can be used in conjunction with
11343      * the {@link #monthNames} array to retrieve the textual day name.
11344      * Example:
11345      * <pre><code>
11346 var dt = new Date('1/10/2007');
11347 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
11348 </code></pre>
11349      * @return {Number} The day number (0-6).
11350      */
11351     getLastDayOfMonth : function() {
11352         return this.getLastDateOfMonth().getDay();
11353     },
11354
11355
11356     /**
11357      * Get the date of the first day of the month in which this date resides.
11358      * @return {Date}
11359      */
11360     getFirstDateOfMonth : function() {
11361         return new Date(this.getFullYear(), this.getMonth(), 1);
11362     },
11363
11364     /**
11365      * Get the date of the last day of the month in which this date resides.
11366      * @return {Date}
11367      */
11368     getLastDateOfMonth : function() {
11369         return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
11370     },
11371
11372     /**
11373      * Get the number of days in the current month, adjusted for leap year.
11374      * @return {Number} The number of days in the month.
11375      */
11376     getDaysInMonth: function() {
11377         var daysInMonth = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
11378
11379         return function() { // return a closure for efficiency
11380             var m = this.getMonth();
11381
11382             return m == 1 && this.isLeapYear() ? 29 : daysInMonth[m];
11383         }
11384     }(),
11385
11386     /**
11387      * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
11388      * @return {String} 'st, 'nd', 'rd' or 'th'.
11389      */
11390     getSuffix : function() {
11391         switch (this.getDate()) {
11392             case 1:
11393             case 21:
11394             case 31:
11395                 return "st";
11396             case 2:
11397             case 22:
11398                 return "nd";
11399             case 3:
11400             case 23:
11401                 return "rd";
11402             default:
11403                 return "th";
11404         }
11405     },
11406
11407     /**
11408      * Creates and returns a new Date instance with the exact same date value as the called instance.
11409      * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
11410      * variable will also be changed.  When the intention is to create a new variable that will not
11411      * modify the original instance, you should create a clone.
11412      *
11413      * Example of correctly cloning a date:
11414      * <pre><code>
11415 //wrong way:
11416 var orig = new Date('10/1/2006');
11417 var copy = orig;
11418 copy.setDate(5);
11419 document.write(orig);  //returns 'Thu Oct 05 2006'!
11420
11421 //correct way:
11422 var orig = new Date('10/1/2006');
11423 var copy = orig.clone();
11424 copy.setDate(5);
11425 document.write(orig);  //returns 'Thu Oct 01 2006'
11426 </code></pre>
11427      * @return {Date} The new Date instance.
11428      */
11429     clone : function() {
11430         return new Date(this.getTime());
11431     },
11432
11433     /**
11434      * Checks if the current date is affected by Daylight Saving Time (DST).
11435      * @return {Boolean} True if the current date is affected by DST.
11436      */
11437     isDST : function() {
11438         // adapted from http://extjs.com/forum/showthread.php?p=247172#post247172
11439         // courtesy of @geoffrey.mcgill
11440         return new Date(this.getFullYear(), 0, 1).getTimezoneOffset() != this.getTimezoneOffset();
11441     },
11442
11443     /**
11444      * Attempts to clear all time information from this Date by setting the time to midnight of the same day,
11445      * automatically adjusting for Daylight Saving Time (DST) where applicable.
11446      * (note: DST timezone information for the browser's host operating system is assumed to be up-to-date)
11447      * @param {Boolean} clone true to create a clone of this date, clear the time and return it (defaults to false).
11448      * @return {Date} this or the clone.
11449      */
11450     clearTime : function(clone) {
11451         if (clone) {
11452             return this.clone().clearTime();
11453         }
11454
11455         // get current date before clearing time
11456         var d = this.getDate();
11457
11458         // clear time
11459         this.setHours(0);
11460         this.setMinutes(0);
11461         this.setSeconds(0);
11462         this.setMilliseconds(0);
11463
11464         if (this.getDate() != d) { // account for DST (i.e. day of month changed when setting hour = 0)
11465             // note: DST adjustments are assumed to occur in multiples of 1 hour (this is almost always the case)
11466             // refer to http://www.timeanddate.com/time/aboutdst.html for the (rare) exceptions to this rule
11467
11468             // increment hour until cloned date == current date
11469             for (var hr = 1, c = this.add(Date.HOUR, hr); c.getDate() != d; hr++, c = this.add(Date.HOUR, hr));
11470
11471             this.setDate(d);
11472             this.setHours(c.getHours());
11473         }
11474
11475         return this;
11476     },
11477
11478     /**
11479      * Provides a convenient method for performing basic date arithmetic. This method
11480      * does not modify the Date instance being called - it creates and returns
11481      * a new Date instance containing the resulting date value.
11482      *
11483      * Examples:
11484      * <pre><code>
11485 // Basic usage:
11486 var dt = new Date('10/29/2006').add(Date.DAY, 5);
11487 document.write(dt); //returns 'Fri Nov 03 2006 00:00:00'
11488
11489 // Negative values will be subtracted:
11490 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
11491 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
11492
11493 // You can even chain several calls together in one line:
11494 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
11495 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
11496 </code></pre>
11497      *
11498      * @param {String} interval A valid date interval enum value.
11499      * @param {Number} value The amount to add to the current date.
11500      * @return {Date} The new Date instance.
11501      */
11502     add : function(interval, value) {
11503         var d = this.clone();
11504         if (!interval || value === 0) return d;
11505
11506         switch(interval.toLowerCase()) {
11507             case Date.MILLI:
11508                 d.setMilliseconds(this.getMilliseconds() + value);
11509                 break;
11510             case Date.SECOND:
11511                 d.setSeconds(this.getSeconds() + value);
11512                 break;
11513             case Date.MINUTE:
11514                 d.setMinutes(this.getMinutes() + value);
11515                 break;
11516             case Date.HOUR:
11517                 d.setHours(this.getHours() + value);
11518                 break;
11519             case Date.DAY:
11520                 d.setDate(this.getDate() + value);
11521                 break;
11522             case Date.MONTH:
11523                 var day = this.getDate();
11524                 if (day > 28) {
11525                     day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
11526                 }
11527                 d.setDate(day);
11528                 d.setMonth(this.getMonth() + value);
11529                 break;
11530             case Date.YEAR:
11531                 d.setFullYear(this.getFullYear() + value);
11532                 break;
11533         }
11534         return d;
11535     },
11536
11537     /**
11538      * Checks if this date falls on or between the given start and end dates.
11539      * @param {Date} start Start date
11540      * @param {Date} end End date
11541      * @return {Boolean} true if this date falls on or between the given start and end dates.
11542      */
11543     between : function(start, end) {
11544         var t = this.getTime();
11545         return start.getTime() <= t && t <= end.getTime();
11546     }
11547 });
11548
11549
11550 /**
11551  * Formats a date given the supplied format string.
11552  * @param {String} format The format string.
11553  * @return {String} The formatted date.
11554  * @method format
11555  */
11556 Date.prototype.format = Date.prototype.dateFormat;
11557
11558
11559 // private
11560 if (Ext.isSafari && (navigator.userAgent.match(/WebKit\/(\d+)/)[1] || NaN) < 420) {
11561     Ext.apply(Date.prototype, {
11562         _xMonth : Date.prototype.setMonth,
11563         _xDate  : Date.prototype.setDate,
11564
11565         // Bug in Safari 1.3, 2.0 (WebKit build < 420)
11566         // Date.setMonth does not work consistently if iMonth is not 0-11
11567         setMonth : function(num) {
11568             if (num <= -1) {
11569                 var n = Math.ceil(-num),
11570                     back_year = Math.ceil(n / 12),
11571                     month = (n % 12) ? 12 - n % 12 : 0;
11572
11573                 this.setFullYear(this.getFullYear() - back_year);
11574
11575                 return this._xMonth(month);
11576             } else {
11577                 return this._xMonth(num);
11578             }
11579         },
11580
11581         // Bug in setDate() method (resolved in WebKit build 419.3, so to be safe we target Webkit builds < 420)
11582         // The parameter for Date.setDate() is converted to a signed byte integer in Safari
11583         // http://brianary.blogspot.com/2006/03/safari-date-bug.html
11584         setDate : function(d) {
11585             // use setTime() to workaround setDate() bug
11586             // subtract current day of month in milliseconds, then add desired day of month in milliseconds
11587             return this.setTime(this.getTime() - (this.getDate() - d) * 864e5);
11588         }
11589     });
11590 }
11591
11592
11593
11594 /* Some basic Date tests... (requires Firebug)
11595
11596 Date.parseDate('', 'c'); // call Date.parseDate() once to force computation of regex string so we can console.log() it
11597 console.log('Insane Regex for "c" format: %o', Date.parseCodes.c.s); // view the insane regex for the "c" format specifier
11598
11599 // standard tests
11600 console.group('Standard Date.parseDate() Tests');
11601     console.log('Date.parseDate("2009-01-05T11:38:56", "c")               = %o', Date.parseDate("2009-01-05T11:38:56", "c")); // assumes browser's timezone setting
11602     console.log('Date.parseDate("2009-02-04T12:37:55.001000", "c")        = %o', Date.parseDate("2009-02-04T12:37:55.001000", "c")); // assumes browser's timezone setting
11603     console.log('Date.parseDate("2009-03-03T13:36:54,101000Z", "c")       = %o', Date.parseDate("2009-03-03T13:36:54,101000Z", "c")); // UTC
11604     console.log('Date.parseDate("2009-04-02T14:35:53.901000-0530", "c")   = %o', Date.parseDate("2009-04-02T14:35:53.901000-0530", "c")); // GMT-0530
11605     console.log('Date.parseDate("2009-05-01T15:34:52,9876000+08:00", "c") = %o', Date.parseDate("2009-05-01T15:34:52,987600+08:00", "c")); // GMT+08:00
11606 console.groupEnd();
11607
11608 // ISO-8601 format as specified in http://www.w3.org/TR/NOTE-datetime
11609 // -- accepts ALL 6 levels of date-time granularity
11610 console.group('ISO-8601 Granularity Test (see http://www.w3.org/TR/NOTE-datetime)');
11611     console.log('Date.parseDate("1997", "c")                              = %o', Date.parseDate("1997", "c")); // YYYY (e.g. 1997)
11612     console.log('Date.parseDate("1997-07", "c")                           = %o', Date.parseDate("1997-07", "c")); // YYYY-MM (e.g. 1997-07)
11613     console.log('Date.parseDate("1997-07-16", "c")                        = %o', Date.parseDate("1997-07-16", "c")); // YYYY-MM-DD (e.g. 1997-07-16)
11614     console.log('Date.parseDate("1997-07-16T19:20+01:00", "c")            = %o', Date.parseDate("1997-07-16T19:20+01:00", "c")); // YYYY-MM-DDThh:mmTZD (e.g. 1997-07-16T19:20+01:00)
11615     console.log('Date.parseDate("1997-07-16T19:20:30+01:00", "c")         = %o', Date.parseDate("1997-07-16T19:20:30+01:00", "c")); // YYYY-MM-DDThh:mm:ssTZD (e.g. 1997-07-16T19:20:30+01:00)
11616     console.log('Date.parseDate("1997-07-16T19:20:30.45+01:00", "c")      = %o', Date.parseDate("1997-07-16T19:20:30.45+01:00", "c")); // YYYY-MM-DDThh:mm:ss.sTZD (e.g. 1997-07-16T19:20:30.45+01:00)
11617     console.log('Date.parseDate("1997-07-16 19:20:30.45+01:00", "c")      = %o', Date.parseDate("1997-07-16 19:20:30.45+01:00", "c")); // YYYY-MM-DD hh:mm:ss.sTZD (e.g. 1997-07-16T19:20:30.45+01:00)
11618     console.log('Date.parseDate("1997-13-16T19:20:30.45+01:00", "c", true)= %o', Date.parseDate("1997-13-16T19:20:30.45+01:00", "c", true)); // strict date parsing with invalid month value
11619 console.groupEnd();
11620
11621 //*//**
11622  * @class Ext.util.MixedCollection
11623  * @extends Ext.util.Observable
11624  * A Collection class that maintains both numeric indexes and keys and exposes events.
11625  * @constructor
11626  * @param {Boolean} allowFunctions Specify <tt>true</tt> if the {@link #addAll}
11627  * function should add function references to the collection. Defaults to
11628  * <tt>false</tt>.
11629  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
11630  * and return the key value for that item.  This is used when available to look up the key on items that
11631  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
11632  * equivalent to providing an implementation for the {@link #getKey} method.
11633  */
11634 Ext.util.MixedCollection = function(allowFunctions, keyFn){
11635     this.items = [];
11636     this.map = {};
11637     this.keys = [];
11638     this.length = 0;
11639     this.addEvents(
11640         /**
11641          * @event clear
11642          * Fires when the collection is cleared.
11643          */
11644         'clear',
11645         /**
11646          * @event add
11647          * Fires when an item is added to the collection.
11648          * @param {Number} index The index at which the item was added.
11649          * @param {Object} o The item added.
11650          * @param {String} key The key associated with the added item.
11651          */
11652         'add',
11653         /**
11654          * @event replace
11655          * Fires when an item is replaced in the collection.
11656          * @param {String} key he key associated with the new added.
11657          * @param {Object} old The item being replaced.
11658          * @param {Object} new The new item.
11659          */
11660         'replace',
11661         /**
11662          * @event remove
11663          * Fires when an item is removed from the collection.
11664          * @param {Object} o The item being removed.
11665          * @param {String} key (optional) The key associated with the removed item.
11666          */
11667         'remove',
11668         'sort'
11669     );
11670     this.allowFunctions = allowFunctions === true;
11671     if(keyFn){
11672         this.getKey = keyFn;
11673     }
11674     Ext.util.MixedCollection.superclass.constructor.call(this);
11675 };
11676
11677 Ext.extend(Ext.util.MixedCollection, Ext.util.Observable, {
11678
11679     /**
11680      * @cfg {Boolean} allowFunctions Specify <tt>true</tt> if the {@link #addAll}
11681      * function should add function references to the collection. Defaults to
11682      * <tt>false</tt>.
11683      */
11684     allowFunctions : false,
11685
11686     /**
11687      * Adds an item to the collection. Fires the {@link #add} event when complete.
11688      * @param {String} key <p>The key to associate with the item, or the new item.</p>
11689      * <p>If a {@link #getKey} implementation was specified for this MixedCollection,
11690      * or if the key of the stored items is in a property called <tt><b>id</b></tt>,
11691      * the MixedCollection will be able to <i>derive</i> the key for the new item.
11692      * In this case just pass the new item in this parameter.</p>
11693      * @param {Object} o The item to add.
11694      * @return {Object} The item added.
11695      */
11696     add : function(key, o){
11697         if(arguments.length == 1){
11698             o = arguments[0];
11699             key = this.getKey(o);
11700         }
11701         if(typeof key != 'undefined' && key !== null){
11702             var old = this.map[key];
11703             if(typeof old != 'undefined'){
11704                 return this.replace(key, o);
11705             }
11706             this.map[key] = o;
11707         }
11708         this.length++;
11709         this.items.push(o);
11710         this.keys.push(key);
11711         this.fireEvent('add', this.length-1, o, key);
11712         return o;
11713     },
11714
11715     /**
11716       * MixedCollection has a generic way to fetch keys if you implement getKey.  The default implementation
11717       * simply returns <b><code>item.id</code></b> but you can provide your own implementation
11718       * to return a different value as in the following examples:<pre><code>
11719 // normal way
11720 var mc = new Ext.util.MixedCollection();
11721 mc.add(someEl.dom.id, someEl);
11722 mc.add(otherEl.dom.id, otherEl);
11723 //and so on
11724
11725 // using getKey
11726 var mc = new Ext.util.MixedCollection();
11727 mc.getKey = function(el){
11728    return el.dom.id;
11729 };
11730 mc.add(someEl);
11731 mc.add(otherEl);
11732
11733 // or via the constructor
11734 var mc = new Ext.util.MixedCollection(false, function(el){
11735    return el.dom.id;
11736 });
11737 mc.add(someEl);
11738 mc.add(otherEl);
11739      * </code></pre>
11740      * @param {Object} item The item for which to find the key.
11741      * @return {Object} The key for the passed item.
11742      */
11743     getKey : function(o){
11744          return o.id;
11745     },
11746
11747     /**
11748      * Replaces an item in the collection. Fires the {@link #replace} event when complete.
11749      * @param {String} key <p>The key associated with the item to replace, or the replacement item.</p>
11750      * <p>If you supplied a {@link #getKey} implementation for this MixedCollection, or if the key
11751      * of your stored items is in a property called <tt><b>id</b></tt>, then the MixedCollection
11752      * will be able to <i>derive</i> the key of the replacement item. If you want to replace an item
11753      * with one having the same key value, then just pass the replacement item in this parameter.</p>
11754      * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate
11755      * with that key.
11756      * @return {Object}  The new item.
11757      */
11758     replace : function(key, o){
11759         if(arguments.length == 1){
11760             o = arguments[0];
11761             key = this.getKey(o);
11762         }
11763         var old = this.map[key];
11764         if(typeof key == 'undefined' || key === null || typeof old == 'undefined'){
11765              return this.add(key, o);
11766         }
11767         var index = this.indexOfKey(key);
11768         this.items[index] = o;
11769         this.map[key] = o;
11770         this.fireEvent('replace', key, old, o);
11771         return o;
11772     },
11773
11774     /**
11775      * Adds all elements of an Array or an Object to the collection.
11776      * @param {Object/Array} objs An Object containing properties which will be added
11777      * to the collection, or an Array of values, each of which are added to the collection.
11778      * Functions references will be added to the collection if <code>{@link #allowFunctions}</code>
11779      * has been set to <tt>true</tt>.
11780      */
11781     addAll : function(objs){
11782         if(arguments.length > 1 || Ext.isArray(objs)){
11783             var args = arguments.length > 1 ? arguments : objs;
11784             for(var i = 0, len = args.length; i < len; i++){
11785                 this.add(args[i]);
11786             }
11787         }else{
11788             for(var key in objs){
11789                 if(this.allowFunctions || typeof objs[key] != 'function'){
11790                     this.add(key, objs[key]);
11791                 }
11792             }
11793         }
11794     },
11795
11796     /**
11797      * Executes the specified function once for every item in the collection, passing the following arguments:
11798      * <div class="mdetail-params"><ul>
11799      * <li><b>item</b> : Mixed<p class="sub-desc">The collection item</p></li>
11800      * <li><b>index</b> : Number<p class="sub-desc">The item's index</p></li>
11801      * <li><b>length</b> : Number<p class="sub-desc">The total number of items in the collection</p></li>
11802      * </ul></div>
11803      * The function should return a boolean value. Returning false from the function will stop the iteration.
11804      * @param {Function} fn The function to execute for each item.
11805      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the current item in the iteration.
11806      */
11807     each : function(fn, scope){
11808         var items = [].concat(this.items); // each safe for removal
11809         for(var i = 0, len = items.length; i < len; i++){
11810             if(fn.call(scope || items[i], items[i], i, len) === false){
11811                 break;
11812             }
11813         }
11814     },
11815
11816     /**
11817      * Executes the specified function once for every key in the collection, passing each
11818      * key, and its associated item as the first two parameters.
11819      * @param {Function} fn The function to execute for each item.
11820      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the browser window.
11821      */
11822     eachKey : function(fn, scope){
11823         for(var i = 0, len = this.keys.length; i < len; i++){
11824             fn.call(scope || window, this.keys[i], this.items[i], i, len);
11825         }
11826     },
11827
11828     /**
11829      * Returns the first item in the collection which elicits a true return value from the
11830      * passed selection function.
11831      * @param {Function} fn The selection function to execute for each item.
11832      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the browser window.
11833      * @return {Object} The first item in the collection which returned true from the selection function.
11834      */
11835     find : function(fn, scope){
11836         for(var i = 0, len = this.items.length; i < len; i++){
11837             if(fn.call(scope || window, this.items[i], this.keys[i])){
11838                 return this.items[i];
11839             }
11840         }
11841         return null;
11842     },
11843
11844     /**
11845      * Inserts an item at the specified index in the collection. Fires the {@link #add} event when complete.
11846      * @param {Number} index The index to insert the item at.
11847      * @param {String} key The key to associate with the new item, or the item itself.
11848      * @param {Object} o (optional) If the second parameter was a key, the new item.
11849      * @return {Object} The item inserted.
11850      */
11851     insert : function(index, key, o){
11852         if(arguments.length == 2){
11853             o = arguments[1];
11854             key = this.getKey(o);
11855         }
11856         if(this.containsKey(key)){
11857             this.suspendEvents();
11858             this.removeKey(key);
11859             this.resumeEvents();
11860         }
11861         if(index >= this.length){
11862             return this.add(key, o);
11863         }
11864         this.length++;
11865         this.items.splice(index, 0, o);
11866         if(typeof key != 'undefined' && key !== null){
11867             this.map[key] = o;
11868         }
11869         this.keys.splice(index, 0, key);
11870         this.fireEvent('add', index, o, key);
11871         return o;
11872     },
11873
11874     /**
11875      * Remove an item from the collection.
11876      * @param {Object} o The item to remove.
11877      * @return {Object} The item removed or false if no item was removed.
11878      */
11879     remove : function(o){
11880         return this.removeAt(this.indexOf(o));
11881     },
11882
11883     /**
11884      * Remove an item from a specified index in the collection. Fires the {@link #remove} event when complete.
11885      * @param {Number} index The index within the collection of the item to remove.
11886      * @return {Object} The item removed or false if no item was removed.
11887      */
11888     removeAt : function(index){
11889         if(index < this.length && index >= 0){
11890             this.length--;
11891             var o = this.items[index];
11892             this.items.splice(index, 1);
11893             var key = this.keys[index];
11894             if(typeof key != 'undefined'){
11895                 delete this.map[key];
11896             }
11897             this.keys.splice(index, 1);
11898             this.fireEvent('remove', o, key);
11899             return o;
11900         }
11901         return false;
11902     },
11903
11904     /**
11905      * Removed an item associated with the passed key fom the collection.
11906      * @param {String} key The key of the item to remove.
11907      * @return {Object} The item removed or false if no item was removed.
11908      */
11909     removeKey : function(key){
11910         return this.removeAt(this.indexOfKey(key));
11911     },
11912
11913     /**
11914      * Returns the number of items in the collection.
11915      * @return {Number} the number of items in the collection.
11916      */
11917     getCount : function(){
11918         return this.length;
11919     },
11920
11921     /**
11922      * Returns index within the collection of the passed Object.
11923      * @param {Object} o The item to find the index of.
11924      * @return {Number} index of the item. Returns -1 if not found.
11925      */
11926     indexOf : function(o){
11927         return this.items.indexOf(o);
11928     },
11929
11930     /**
11931      * Returns index within the collection of the passed key.
11932      * @param {String} key The key to find the index of.
11933      * @return {Number} index of the key.
11934      */
11935     indexOfKey : function(key){
11936         return this.keys.indexOf(key);
11937     },
11938
11939     /**
11940      * Returns the item associated with the passed key OR index.
11941      * Key has priority over index.  This is the equivalent
11942      * of calling {@link #key} first, then if nothing matched calling {@link #itemAt}.
11943      * @param {String/Number} key The key or index of the item.
11944      * @return {Object} If the item is found, returns the item.  If the item was not found, returns <tt>undefined</tt>.
11945      * If an item was found, but is a Class, returns <tt>null</tt>.
11946      */
11947     item : function(key){
11948         var mk = this.map[key],
11949             item = mk !== undefined ? mk : (typeof key == 'number') ? this.items[key] : undefined;
11950         return !Ext.isFunction(item) || this.allowFunctions ? item : null; // for prototype!
11951     },
11952
11953     /**
11954      * Returns the item at the specified index.
11955      * @param {Number} index The index of the item.
11956      * @return {Object} The item at the specified index.
11957      */
11958     itemAt : function(index){
11959         return this.items[index];
11960     },
11961
11962     /**
11963      * Returns the item associated with the passed key.
11964      * @param {String/Number} key The key of the item.
11965      * @return {Object} The item associated with the passed key.
11966      */
11967     key : function(key){
11968         return this.map[key];
11969     },
11970
11971     /**
11972      * Returns true if the collection contains the passed Object as an item.
11973      * @param {Object} o  The Object to look for in the collection.
11974      * @return {Boolean} True if the collection contains the Object as an item.
11975      */
11976     contains : function(o){
11977         return this.indexOf(o) != -1;
11978     },
11979
11980     /**
11981      * Returns true if the collection contains the passed Object as a key.
11982      * @param {String} key The key to look for in the collection.
11983      * @return {Boolean} True if the collection contains the Object as a key.
11984      */
11985     containsKey : function(key){
11986         return typeof this.map[key] != 'undefined';
11987     },
11988
11989     /**
11990      * Removes all items from the collection.  Fires the {@link #clear} event when complete.
11991      */
11992     clear : function(){
11993         this.length = 0;
11994         this.items = [];
11995         this.keys = [];
11996         this.map = {};
11997         this.fireEvent('clear');
11998     },
11999
12000     /**
12001      * Returns the first item in the collection.
12002      * @return {Object} the first item in the collection..
12003      */
12004     first : function(){
12005         return this.items[0];
12006     },
12007
12008     /**
12009      * Returns the last item in the collection.
12010      * @return {Object} the last item in the collection..
12011      */
12012     last : function(){
12013         return this.items[this.length-1];
12014     },
12015
12016     /**
12017      * @private
12018      * @param {String} property Property to sort by ('key', 'value', or 'index')
12019      * @param {String} dir (optional) Direction to sort 'ASC' or 'DESC'. Defaults to 'ASC'.
12020      * @param {Function} fn (optional) Comparison function that defines the sort order.
12021      * Defaults to sorting by numeric value.
12022      */
12023     _sort : function(property, dir, fn){
12024         var i,
12025             len,
12026             dsc = String(dir).toUpperCase() == 'DESC' ? -1 : 1,
12027             c = [], k = this.keys, items = this.items;
12028
12029         fn = fn || function(a, b){
12030             return a-b;
12031         };
12032         for(i = 0, len = items.length; i < len; i++){
12033             c[c.length] = {key: k[i], value: items[i], index: i};
12034         }
12035         c.sort(function(a, b){
12036             var v = fn(a[property], b[property]) * dsc;
12037             if(v === 0){
12038                 v = (a.index < b.index ? -1 : 1);
12039             }
12040             return v;
12041         });
12042         for(i = 0, len = c.length; i < len; i++){
12043             items[i] = c[i].value;
12044             k[i] = c[i].key;
12045         }
12046         this.fireEvent('sort', this);
12047     },
12048
12049     /**
12050      * Sorts this collection by <b>item</b> value with the passed comparison function.
12051      * @param {String} direction (optional) 'ASC' or 'DESC'. Defaults to 'ASC'.
12052      * @param {Function} fn (optional) Comparison function that defines the sort order.
12053      * Defaults to sorting by numeric value.
12054      */
12055     sort : function(dir, fn){
12056         this._sort('value', dir, fn);
12057     },
12058
12059     /**
12060      * Sorts this collection by <b>key</b>s.
12061      * @param {String} direction (optional) 'ASC' or 'DESC'. Defaults to 'ASC'.
12062      * @param {Function} fn (optional) Comparison function that defines the sort order.
12063      * Defaults to sorting by case insensitive string.
12064      */
12065     keySort : function(dir, fn){
12066         this._sort('key', dir, fn || function(a, b){
12067             var v1 = String(a).toUpperCase(), v2 = String(b).toUpperCase();
12068             return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
12069         });
12070     },
12071
12072     /**
12073      * Returns a range of items in this collection
12074      * @param {Number} startIndex (optional) The starting index. Defaults to 0.
12075      * @param {Number} endIndex (optional) The ending index. Defaults to the last item.
12076      * @return {Array} An array of items
12077      */
12078     getRange : function(start, end){
12079         var items = this.items;
12080         if(items.length < 1){
12081             return [];
12082         }
12083         start = start || 0;
12084         end = Math.min(typeof end == 'undefined' ? this.length-1 : end, this.length-1);
12085         var i, r = [];
12086         if(start <= end){
12087             for(i = start; i <= end; i++) {
12088                 r[r.length] = items[i];
12089             }
12090         }else{
12091             for(i = start; i >= end; i--) {
12092                 r[r.length] = items[i];
12093             }
12094         }
12095         return r;
12096     },
12097
12098     /**
12099      * Filter the <i>objects</i> in this collection by a specific property.
12100      * Returns a new collection that has been filtered.
12101      * @param {String} property A property on your objects
12102      * @param {String/RegExp} value Either string that the property values
12103      * should start with or a RegExp to test against the property
12104      * @param {Boolean} anyMatch (optional) True to match any part of the string, not just the beginning
12105      * @param {Boolean} caseSensitive (optional) True for case sensitive comparison (defaults to False).
12106      * @return {MixedCollection} The new filtered collection
12107      */
12108     filter : function(property, value, anyMatch, caseSensitive){
12109         if(Ext.isEmpty(value, false)){
12110             return this.clone();
12111         }
12112         value = this.createValueMatcher(value, anyMatch, caseSensitive);
12113         return this.filterBy(function(o){
12114             return o && value.test(o[property]);
12115         });
12116     },
12117
12118     /**
12119      * Filter by a function. Returns a <i>new</i> collection that has been filtered.
12120      * The passed function will be called with each object in the collection.
12121      * If the function returns true, the value is included otherwise it is filtered.
12122      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
12123      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to this MixedCollection.
12124      * @return {MixedCollection} The new filtered collection
12125      */
12126     filterBy : function(fn, scope){
12127         var r = new Ext.util.MixedCollection();
12128         r.getKey = this.getKey;
12129         var k = this.keys, it = this.items;
12130         for(var i = 0, len = it.length; i < len; i++){
12131             if(fn.call(scope||this, it[i], k[i])){
12132                 r.add(k[i], it[i]);
12133             }
12134         }
12135         return r;
12136     },
12137
12138     /**
12139      * Finds the index of the first matching object in this collection by a specific property/value.
12140      * @param {String} property The name of a property on your objects.
12141      * @param {String/RegExp} value A string that the property values
12142      * should start with or a RegExp to test against the property.
12143      * @param {Number} start (optional) The index to start searching at (defaults to 0).
12144      * @param {Boolean} anyMatch (optional) True to match any part of the string, not just the beginning.
12145      * @param {Boolean} caseSensitive (optional) True for case sensitive comparison.
12146      * @return {Number} The matched index or -1
12147      */
12148     findIndex : function(property, value, start, anyMatch, caseSensitive){
12149         if(Ext.isEmpty(value, false)){
12150             return -1;
12151         }
12152         value = this.createValueMatcher(value, anyMatch, caseSensitive);
12153         return this.findIndexBy(function(o){
12154             return o && value.test(o[property]);
12155         }, null, start);
12156     },
12157
12158     /**
12159      * Find the index of the first matching object in this collection by a function.
12160      * If the function returns <i>true</i> it is considered a match.
12161      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key).
12162      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to this MixedCollection.
12163      * @param {Number} start (optional) The index to start searching at (defaults to 0).
12164      * @return {Number} The matched index or -1
12165      */
12166     findIndexBy : function(fn, scope, start){
12167         var k = this.keys, it = this.items;
12168         for(var i = (start||0), len = it.length; i < len; i++){
12169             if(fn.call(scope||this, it[i], k[i])){
12170                 return i;
12171             }
12172         }
12173         return -1;
12174     },
12175
12176     // private
12177     createValueMatcher : function(value, anyMatch, caseSensitive, exactMatch) {
12178         if (!value.exec) { // not a regex
12179             var er = Ext.escapeRe;
12180             value = String(value);
12181             if (anyMatch === true) {
12182                 value = er(value);
12183             } else {
12184                 value = '^' + er(value);
12185                 if (exactMatch === true) {
12186                     value += '$';
12187                 }
12188             }
12189             value = new RegExp(value, caseSensitive ? '' : 'i');
12190          }
12191          return value;
12192     },
12193
12194     /**
12195      * Creates a shallow copy of this collection
12196      * @return {MixedCollection}
12197      */
12198     clone : function(){
12199         var r = new Ext.util.MixedCollection();
12200         var k = this.keys, it = this.items;
12201         for(var i = 0, len = it.length; i < len; i++){
12202             r.add(k[i], it[i]);
12203         }
12204         r.getKey = this.getKey;
12205         return r;
12206     }
12207 });
12208 /**
12209  * This method calls {@link #item item()}.
12210  * Returns the item associated with the passed key OR index. Key has priority
12211  * over index.  This is the equivalent of calling {@link #key} first, then if
12212  * nothing matched calling {@link #itemAt}.
12213  * @param {String/Number} key The key or index of the item.
12214  * @return {Object} If the item is found, returns the item.  If the item was
12215  * not found, returns <tt>undefined</tt>. If an item was found, but is a Class,
12216  * returns <tt>null</tt>.
12217  */
12218 Ext.util.MixedCollection.prototype.get = Ext.util.MixedCollection.prototype.item;/**
12219  * @class Ext.util.JSON
12220  * Modified version of Douglas Crockford"s json.js that doesn"t
12221  * mess with the Object prototype
12222  * http://www.json.org/js.html
12223  * @singleton
12224  */
12225 Ext.util.JSON = new (function(){
12226     var useHasOwn = !!{}.hasOwnProperty,
12227         isNative = function() {
12228             var useNative = null;
12229
12230             return function() {
12231                 if (useNative === null) {
12232                     useNative = Ext.USE_NATIVE_JSON && window.JSON && JSON.toString() == '[object JSON]';
12233                 }
12234         
12235                 return useNative;
12236             };
12237         }(),
12238         pad = function(n) {
12239             return n < 10 ? "0" + n : n;
12240         },
12241         doDecode = function(json){
12242             return eval("(" + json + ')');    
12243         },
12244         doEncode = function(o){
12245             if(!Ext.isDefined(o) || o === null){
12246                 return "null";
12247             }else if(Ext.isArray(o)){
12248                 return encodeArray(o);
12249             }else if(Ext.isDate(o)){
12250                 return Ext.util.JSON.encodeDate(o);
12251             }else if(Ext.isString(o)){
12252                 return encodeString(o);
12253             }else if(typeof o == "number"){
12254                 //don't use isNumber here, since finite checks happen inside isNumber
12255                 return isFinite(o) ? String(o) : "null";
12256             }else if(Ext.isBoolean(o)){
12257                 return String(o);
12258             }else {
12259                 var a = ["{"], b, i, v;
12260                 for (i in o) {
12261                     // don't encode DOM objects
12262                     if(!o.getElementsByTagName){
12263                         if(!useHasOwn || o.hasOwnProperty(i)) {
12264                             v = o[i];
12265                             switch (typeof v) {
12266                             case "undefined":
12267                             case "function":
12268                             case "unknown":
12269                                 break;
12270                             default:
12271                                 if(b){
12272                                     a.push(',');
12273                                 }
12274                                 a.push(doEncode(i), ":",
12275                                         v === null ? "null" : doEncode(v));
12276                                 b = true;
12277                             }
12278                         }
12279                     }
12280                 }
12281                 a.push("}");
12282                 return a.join("");
12283             }    
12284         },
12285         m = {
12286             "\b": '\\b',
12287             "\t": '\\t',
12288             "\n": '\\n',
12289             "\f": '\\f',
12290             "\r": '\\r',
12291             '"' : '\\"',
12292             "\\": '\\\\'
12293         },
12294         encodeString = function(s){
12295             if (/["\\\x00-\x1f]/.test(s)) {
12296                 return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
12297                     var c = m[b];
12298                     if(c){
12299                         return c;
12300                     }
12301                     c = b.charCodeAt();
12302                     return "\\u00" +
12303                         Math.floor(c / 16).toString(16) +
12304                         (c % 16).toString(16);
12305                 }) + '"';
12306             }
12307             return '"' + s + '"';
12308         },
12309         encodeArray = function(o){
12310             var a = ["["], b, i, l = o.length, v;
12311                 for (i = 0; i < l; i += 1) {
12312                     v = o[i];
12313                     switch (typeof v) {
12314                         case "undefined":
12315                         case "function":
12316                         case "unknown":
12317                             break;
12318                         default:
12319                             if (b) {
12320                                 a.push(',');
12321                             }
12322                             a.push(v === null ? "null" : Ext.util.JSON.encode(v));
12323                             b = true;
12324                     }
12325                 }
12326                 a.push("]");
12327                 return a.join("");
12328         };
12329
12330     /**
12331      * <p>Encodes a Date. This returns the actual string which is inserted into the JSON string as the literal expression.
12332      * <b>The returned value includes enclosing double quotation marks.</b></p>
12333      * <p>The default return format is "yyyy-mm-ddThh:mm:ss".</p>
12334      * <p>To override this:</p><pre><code>
12335 Ext.util.JSON.encodeDate = function(d) {
12336     return d.format('"Y-m-d"');
12337 };
12338 </code></pre>
12339      * @param {Date} d The Date to encode
12340      * @return {String} The string literal to use in a JSON string.
12341      */
12342     this.encodeDate = function(o){
12343         return '"' + o.getFullYear() + "-" +
12344                 pad(o.getMonth() + 1) + "-" +
12345                 pad(o.getDate()) + "T" +
12346                 pad(o.getHours()) + ":" +
12347                 pad(o.getMinutes()) + ":" +
12348                 pad(o.getSeconds()) + '"';
12349     };
12350
12351     /**
12352      * Encodes an Object, Array or other value
12353      * @param {Mixed} o The variable to encode
12354      * @return {String} The JSON string
12355      */
12356     this.encode = function() {
12357         var ec;
12358         return function(o) {
12359             if (!ec) {
12360                 // setup encoding function on first access
12361                 ec = isNative() ? JSON.stringify : doEncode;
12362             }
12363             return ec(o);
12364         };
12365     }();
12366
12367
12368     /**
12369      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError unless the safe option is set.
12370      * @param {String} json The JSON string
12371      * @return {Object} The resulting object
12372      */
12373     this.decode = function() {
12374         var dc;
12375         return function(json) {
12376             if (!dc) {
12377                 // setup decoding function on first access
12378                 dc = isNative() ? JSON.parse : doDecode;
12379             }
12380             return dc(json);
12381         };
12382     }();
12383
12384 })();
12385 /**
12386  * Shorthand for {@link Ext.util.JSON#encode}
12387  * @param {Mixed} o The variable to encode
12388  * @return {String} The JSON string
12389  * @member Ext
12390  * @method encode
12391  */
12392 Ext.encode = Ext.util.JSON.encode;
12393 /**
12394  * Shorthand for {@link Ext.util.JSON#decode}
12395  * @param {String} json The JSON string
12396  * @param {Boolean} safe (optional) Whether to return null or throw an exception if the JSON is invalid.
12397  * @return {Object} The resulting object
12398  * @member Ext
12399  * @method decode
12400  */
12401 Ext.decode = Ext.util.JSON.decode;
12402 /**\r
12403  * @class Ext.util.Format\r
12404  * Reusable data formatting functions\r
12405  * @singleton\r
12406  */\r
12407 Ext.util.Format = function(){\r
12408     var trimRe = /^\s+|\s+$/g,\r
12409         stripTagsRE = /<\/?[^>]+>/gi,\r
12410         stripScriptsRe = /(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig,\r
12411         nl2brRe = /\r?\n/g;\r
12412         \r
12413     return {\r
12414         /**\r
12415          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length\r
12416          * @param {String} value The string to truncate\r
12417          * @param {Number} length The maximum length to allow before truncating\r
12418          * @param {Boolean} word True to try to find a common work break\r
12419          * @return {String} The converted text\r
12420          */\r
12421         ellipsis : function(value, len, word){\r
12422             if(value && value.length > len){\r
12423                 if(word){\r
12424                     var vs = value.substr(0, len - 2),\r
12425                         index = Math.max(vs.lastIndexOf(' '), vs.lastIndexOf('.'), vs.lastIndexOf('!'), vs.lastIndexOf('?'));\r
12426                     if(index == -1 || index < (len - 15)){\r
12427                         return value.substr(0, len - 3) + "...";\r
12428                     }else{\r
12429                         return vs.substr(0, index) + "...";\r
12430                     }\r
12431                 } else{\r
12432                     return value.substr(0, len - 3) + "...";\r
12433                 }\r
12434             }\r
12435             return value;\r
12436         },\r
12437 \r
12438         /**\r
12439          * Checks a reference and converts it to empty string if it is undefined\r
12440          * @param {Mixed} value Reference to check\r
12441          * @return {Mixed} Empty string if converted, otherwise the original value\r
12442          */\r
12443         undef : function(value){\r
12444             return value !== undefined ? value : "";\r
12445         },\r
12446 \r
12447         /**\r
12448          * Checks a reference and converts it to the default value if it's empty\r
12449          * @param {Mixed} value Reference to check\r
12450          * @param {String} defaultValue The value to insert of it's undefined (defaults to "")\r
12451          * @return {String}\r
12452          */\r
12453         defaultValue : function(value, defaultValue){\r
12454             return value !== undefined && value !== '' ? value : defaultValue;\r
12455         },\r
12456 \r
12457         /**\r
12458          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.\r
12459          * @param {String} value The string to encode\r
12460          * @return {String} The encoded text\r
12461          */\r
12462         htmlEncode : function(value){\r
12463             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");\r
12464         },\r
12465 \r
12466         /**\r
12467          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.\r
12468          * @param {String} value The string to decode\r
12469          * @return {String} The decoded text\r
12470          */\r
12471         htmlDecode : function(value){\r
12472             return !value ? value : String(value).replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"').replace(/&amp;/g, "&");\r
12473         },\r
12474 \r
12475         /**\r
12476          * Trims any whitespace from either side of a string\r
12477          * @param {String} value The text to trim\r
12478          * @return {String} The trimmed text\r
12479          */\r
12480         trim : function(value){\r
12481             return String(value).replace(trimRe, "");\r
12482         },\r
12483 \r
12484         /**\r
12485          * Returns a substring from within an original string\r
12486          * @param {String} value The original text\r
12487          * @param {Number} start The start index of the substring\r
12488          * @param {Number} length The length of the substring\r
12489          * @return {String} The substring\r
12490          */\r
12491         substr : function(value, start, length){\r
12492             return String(value).substr(start, length);\r
12493         },\r
12494 \r
12495         /**\r
12496          * Converts a string to all lower case letters\r
12497          * @param {String} value The text to convert\r
12498          * @return {String} The converted text\r
12499          */\r
12500         lowercase : function(value){\r
12501             return String(value).toLowerCase();\r
12502         },\r
12503 \r
12504         /**\r
12505          * Converts a string to all upper case letters\r
12506          * @param {String} value The text to convert\r
12507          * @return {String} The converted text\r
12508          */\r
12509         uppercase : function(value){\r
12510             return String(value).toUpperCase();\r
12511         },\r
12512 \r
12513         /**\r
12514          * Converts the first character only of a string to upper case\r
12515          * @param {String} value The text to convert\r
12516          * @return {String} The converted text\r
12517          */\r
12518         capitalize : function(value){\r
12519             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();\r
12520         },\r
12521 \r
12522         // private\r
12523         call : function(value, fn){\r
12524             if(arguments.length > 2){\r
12525                 var args = Array.prototype.slice.call(arguments, 2);\r
12526                 args.unshift(value);\r
12527                 return eval(fn).apply(window, args);\r
12528             }else{\r
12529                 return eval(fn).call(window, value);\r
12530             }\r
12531         },\r
12532 \r
12533         /**\r
12534          * Format a number as US currency\r
12535          * @param {Number/String} value The numeric value to format\r
12536          * @return {String} The formatted currency string\r
12537          */\r
12538         usMoney : function(v){\r
12539             v = (Math.round((v-0)*100))/100;\r
12540             v = (v == Math.floor(v)) ? v + ".00" : ((v*10 == Math.floor(v*10)) ? v + "0" : v);\r
12541             v = String(v);\r
12542             var ps = v.split('.'),\r
12543                 whole = ps[0],\r
12544                 sub = ps[1] ? '.'+ ps[1] : '.00',\r
12545                 r = /(\d+)(\d{3})/;\r
12546             while (r.test(whole)) {\r
12547                 whole = whole.replace(r, '$1' + ',' + '$2');\r
12548             }\r
12549             v = whole + sub;\r
12550             if(v.charAt(0) == '-'){\r
12551                 return '-$' + v.substr(1);\r
12552             }\r
12553             return "$" +  v;\r
12554         },\r
12555 \r
12556         /**\r
12557          * Parse a value into a formatted date using the specified format pattern.\r
12558          * @param {String/Date} value The value to format (Strings must conform to the format expected by the javascript Date object's <a href="http://www.w3schools.com/jsref/jsref_parse.asp">parse()</a> method)\r
12559          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')\r
12560          * @return {String} The formatted date string\r
12561          */\r
12562         date : function(v, format){\r
12563             if(!v){\r
12564                 return "";\r
12565             }\r
12566             if(!Ext.isDate(v)){\r
12567                 v = new Date(Date.parse(v));\r
12568             }\r
12569             return v.dateFormat(format || "m/d/Y");\r
12570         },\r
12571 \r
12572         /**\r
12573          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently\r
12574          * @param {String} format Any valid date format string\r
12575          * @return {Function} The date formatting function\r
12576          */\r
12577         dateRenderer : function(format){\r
12578             return function(v){\r
12579                 return Ext.util.Format.date(v, format);\r
12580             };\r
12581         },\r
12582         \r
12583         /**\r
12584          * Strips all HTML tags\r
12585          * @param {Mixed} value The text from which to strip tags\r
12586          * @return {String} The stripped text\r
12587          */\r
12588         stripTags : function(v){\r
12589             return !v ? v : String(v).replace(stripTagsRE, "");\r
12590         },\r
12591 \r
12592         /**\r
12593          * Strips all script tags\r
12594          * @param {Mixed} value The text from which to strip script tags\r
12595          * @return {String} The stripped text\r
12596          */\r
12597         stripScripts : function(v){\r
12598             return !v ? v : String(v).replace(stripScriptsRe, "");\r
12599         },\r
12600 \r
12601         /**\r
12602          * Simple format for a file size (xxx bytes, xxx KB, xxx MB)\r
12603          * @param {Number/String} size The numeric value to format\r
12604          * @return {String} The formatted file size\r
12605          */\r
12606         fileSize : function(size){\r
12607             if(size < 1024) {\r
12608                 return size + " bytes";\r
12609             } else if(size < 1048576) {\r
12610                 return (Math.round(((size*10) / 1024))/10) + " KB";\r
12611             } else {\r
12612                 return (Math.round(((size*10) / 1048576))/10) + " MB";\r
12613             }\r
12614         },\r
12615 \r
12616         /**\r
12617          * It does simple math for use in a template, for example:<pre><code>\r
12618          * var tpl = new Ext.Template('{value} * 10 = {value:math("* 10")}');\r
12619          * </code></pre>\r
12620          * @return {Function} A function that operates on the passed value.\r
12621          */\r
12622         math : function(){\r
12623             var fns = {};\r
12624             return function(v, a){\r
12625                 if(!fns[a]){\r
12626                     fns[a] = new Function('v', 'return v ' + a + ';');\r
12627                 }\r
12628                 return fns[a](v);\r
12629             }\r
12630         }(),\r
12631 \r
12632         /**\r
12633          * Rounds the passed number to the required decimal precision.\r
12634          * @param {Number/String} value The numeric value to round.\r
12635          * @param {Number} precision The number of decimal places to which to round the first parameter's value.\r
12636          * @return {Number} The rounded value.\r
12637          */\r
12638         round : function(value, precision) {\r
12639             var result = Number(value);\r
12640             if (typeof precision == 'number') {\r
12641                 precision = Math.pow(10, precision);\r
12642                 result = Math.round(value * precision) / precision;\r
12643             }\r
12644             return result;\r
12645         },\r
12646 \r
12647         /**\r
12648          * Formats the number according to the format string.\r
12649          * <div style="margin-left:40px">examples (123456.789):\r
12650          * <div style="margin-left:10px">\r
12651          * 0 - (123456) show only digits, no precision<br>\r
12652          * 0.00 - (123456.78) show only digits, 2 precision<br>\r
12653          * 0.0000 - (123456.7890) show only digits, 4 precision<br>\r
12654          * 0,000 - (123,456) show comma and digits, no precision<br>\r
12655          * 0,000.00 - (123,456.78) show comma and digits, 2 precision<br>\r
12656          * 0,0.00 - (123,456.78) shortcut method, show comma and digits, 2 precision<br>\r
12657          * To reverse the grouping (,) and decimal (.) for international numbers, add /i to the end.\r
12658          * For example: 0.000,00/i\r
12659          * </div></div>\r
12660          * @param {Number} v The number to format.\r
12661          * @param {String} format The way you would like to format this text.\r
12662          * @return {String} The formatted number.\r
12663          */\r
12664         number: function(v, format) {\r
12665             if(!format){\r
12666                         return v;\r
12667                     }\r
12668                     v = Ext.num(v, NaN);\r
12669             if (isNaN(v)){\r
12670                 return '';\r
12671             }\r
12672                     var comma = ',',\r
12673                         dec = '.',\r
12674                         i18n = false,\r
12675                         neg = v < 0;\r
12676                 \r
12677                     v = Math.abs(v);\r
12678                     if(format.substr(format.length - 2) == '/i'){\r
12679                         format = format.substr(0, format.length - 2);\r
12680                         i18n = true;\r
12681                         comma = '.';\r
12682                         dec = ',';\r
12683                     }\r
12684                 \r
12685                     var hasComma = format.indexOf(comma) != -1, \r
12686                         psplit = (i18n ? format.replace(/[^\d\,]/g, '') : format.replace(/[^\d\.]/g, '')).split(dec);\r
12687                 \r
12688                     if(1 < psplit.length){\r
12689                         v = v.toFixed(psplit[1].length);\r
12690                     }else if(2 < psplit.length){\r
12691                         throw ('NumberFormatException: invalid format, formats should have no more than 1 period: ' + format);\r
12692                     }else{\r
12693                         v = v.toFixed(0);\r
12694                     }\r
12695                 \r
12696                     var fnum = v.toString();\r
12697                     if(hasComma){\r
12698                         psplit = fnum.split('.');\r
12699                 \r
12700                         var cnum = psplit[0], parr = [], j = cnum.length, m = Math.floor(j / 3), n = cnum.length % 3 || 3;\r
12701                 \r
12702                         for(var i = 0; i < j; i += n){\r
12703                             if(i != 0){\r
12704                                 n = 3;\r
12705                             }\r
12706                             parr[parr.length] = cnum.substr(i, n);\r
12707                             m -= 1;\r
12708                         }\r
12709                         fnum = parr.join(comma);\r
12710                         if(psplit[1]){\r
12711                             fnum += dec + psplit[1];\r
12712                         }\r
12713                     }\r
12714                 \r
12715                     return (neg ? '-' : '') + format.replace(/[\d,?\.?]+/, fnum);\r
12716         },\r
12717 \r
12718         /**\r
12719          * Returns a number rendering function that can be reused to apply a number format multiple times efficiently\r
12720          * @param {String} format Any valid number format string for {@link #number}\r
12721          * @return {Function} The number formatting function\r
12722          */\r
12723         numberRenderer : function(format){\r
12724             return function(v){\r
12725                 return Ext.util.Format.number(v, format);\r
12726             };\r
12727         },\r
12728 \r
12729         /**\r
12730          * Selectively do a plural form of a word based on a numeric value. For example, in a template,\r
12731          * {commentCount:plural("Comment")}  would result in "1 Comment" if commentCount was 1 or would be "x Comments"\r
12732          * if the value is 0 or greater than 1.\r
12733          * @param {Number} value The value to compare against\r
12734          * @param {String} singular The singular form of the word\r
12735          * @param {String} plural (optional) The plural form of the word (defaults to the singular with an "s")\r
12736          */\r
12737         plural : function(v, s, p){\r
12738             return v +' ' + (v == 1 ? s : (p ? p : s+'s'));\r
12739         },\r
12740         \r
12741         /**\r
12742          * Converts newline characters to the HTML tag &lt;br/>\r
12743          * @param {String} The string value to format.\r
12744          * @return {String} The string with embedded &lt;br/> tags in place of newlines.\r
12745          */\r
12746         nl2br : function(v){\r
12747             return Ext.isEmpty(v) ? '' : v.replace(nl2brRe, '<br/>');\r
12748         }\r
12749     }\r
12750 }();\r
12751 /**
12752  * @class Ext.XTemplate
12753  * @extends Ext.Template
12754  * <p>A template class that supports advanced functionality like:<div class="mdetail-params"><ul>
12755  * <li>Autofilling arrays using templates and sub-templates</li>
12756  * <li>Conditional processing with basic comparison operators</li>
12757  * <li>Basic math function support</li>
12758  * <li>Execute arbitrary inline code with special built-in template variables</li>
12759  * <li>Custom member functions</li>
12760  * <li>Many special tags and built-in operators that aren't defined as part of
12761  * the API, but are supported in the templates that can be created</li>
12762  * </ul></div></p>
12763  * <p>XTemplate provides the templating mechanism built into:<div class="mdetail-params"><ul>
12764  * <li>{@link Ext.DataView}</li>
12765  * <li>{@link Ext.ListView}</li>
12766  * <li>{@link Ext.form.ComboBox}</li>
12767  * <li>{@link Ext.grid.TemplateColumn}</li>
12768  * <li>{@link Ext.grid.GroupingView}</li>
12769  * <li>{@link Ext.menu.Item}</li>
12770  * <li>{@link Ext.layout.MenuLayout}</li>
12771  * <li>{@link Ext.ColorPalette}</li>
12772  * </ul></div></p>
12773  * 
12774  * <p>For example usage {@link #XTemplate see the constructor}.</p>  
12775  *   
12776  * @constructor
12777  * The {@link Ext.Template#Template Ext.Template constructor} describes
12778  * the acceptable parameters to pass to the constructor. The following
12779  * examples demonstrate all of the supported features.</p>
12780  * 
12781  * <div class="mdetail-params"><ul>
12782  * 
12783  * <li><b><u>Sample Data</u></b> 
12784  * <div class="sub-desc">
12785  * <p>This is the data object used for reference in each code example:</p>
12786  * <pre><code>
12787 var data = {
12788     name: 'Jack Slocum',
12789     title: 'Lead Developer',
12790     company: 'Ext JS, LLC',
12791     email: 'jack@extjs.com',
12792     address: '4 Red Bulls Drive',
12793     city: 'Cleveland',
12794     state: 'Ohio',
12795     zip: '44102',
12796     drinks: ['Red Bull', 'Coffee', 'Water'],
12797     kids: [{
12798         name: 'Sara Grace',
12799         age:3
12800     },{
12801         name: 'Zachary',
12802         age:2
12803     },{
12804         name: 'John James',
12805         age:0
12806     }]
12807 };
12808  * </code></pre>
12809  * </div>
12810  * </li>
12811  * 
12812  * 
12813  * <li><b><u>Auto filling of arrays</u></b> 
12814  * <div class="sub-desc">
12815  * <p>The <b><tt>tpl</tt></b> tag and the <b><tt>for</tt></b> operator are used
12816  * to process the provided data object:
12817  * <ul>
12818  * <li>If the value specified in <tt>for</tt> is an array, it will auto-fill,
12819  * repeating the template block inside the <tt>tpl</tt> tag for each item in the
12820  * array.</li>
12821  * <li>If <tt>for="."</tt> is specified, the data object provided is examined.</li>
12822  * <li>While processing an array, the special variable <tt>{#}</tt>
12823  * will provide the current array index + 1 (starts at 1, not 0).</li>
12824  * </ul>
12825  * </p>
12826  * <pre><code>
12827 &lt;tpl <b>for</b>=".">...&lt;/tpl>       // loop through array at root node
12828 &lt;tpl <b>for</b>="foo">...&lt;/tpl>     // loop through array at foo node
12829 &lt;tpl <b>for</b>="foo.bar">...&lt;/tpl> // loop through array at foo.bar node
12830  * </code></pre>
12831  * Using the sample data above:
12832  * <pre><code>
12833 var tpl = new Ext.XTemplate(
12834     '&lt;p>Kids: ',
12835     '&lt;tpl <b>for</b>=".">',       // process the data.kids node
12836         '&lt;p>{#}. {name}&lt;/p>',  // use current array index to autonumber
12837     '&lt;/tpl>&lt;/p>'
12838 );
12839 tpl.overwrite(panel.body, data.kids); // pass the kids property of the data object
12840  * </code></pre>
12841  * <p>An example illustrating how the <b><tt>for</tt></b> property can be leveraged
12842  * to access specified members of the provided data object to populate the template:</p>
12843  * <pre><code>
12844 var tpl = new Ext.XTemplate(
12845     '&lt;p>Name: {name}&lt;/p>',
12846     '&lt;p>Title: {title}&lt;/p>',
12847     '&lt;p>Company: {company}&lt;/p>',
12848     '&lt;p>Kids: ',
12849     '&lt;tpl <b>for="kids"</b>>',     // interrogate the kids property within the data
12850         '&lt;p>{name}&lt;/p>',
12851     '&lt;/tpl>&lt;/p>'
12852 );
12853 tpl.overwrite(panel.body, data);  // pass the root node of the data object
12854  * </code></pre>
12855  * <p>Flat arrays that contain values (and not objects) can be auto-rendered
12856  * using the special <b><tt>{.}</tt></b> variable inside a loop.  This variable
12857  * will represent the value of the array at the current index:</p>
12858  * <pre><code>
12859 var tpl = new Ext.XTemplate(
12860     '&lt;p>{name}\&#39;s favorite beverages:&lt;/p>',
12861     '&lt;tpl for="drinks">',
12862        '&lt;div> - {.}&lt;/div>',
12863     '&lt;/tpl>'
12864 );
12865 tpl.overwrite(panel.body, data);
12866  * </code></pre>
12867  * <p>When processing a sub-template, for example while looping through a child array,
12868  * you can access the parent object's members via the <b><tt>parent</tt></b> object:</p>
12869  * <pre><code>
12870 var tpl = new Ext.XTemplate(
12871     '&lt;p>Name: {name}&lt;/p>',
12872     '&lt;p>Kids: ',
12873     '&lt;tpl for="kids">',
12874         '&lt;tpl if="age > 1">',
12875             '&lt;p>{name}&lt;/p>',
12876             '&lt;p>Dad: {<b>parent</b>.name}&lt;/p>',
12877         '&lt;/tpl>',
12878     '&lt;/tpl>&lt;/p>'
12879 );
12880 tpl.overwrite(panel.body, data);
12881  * </code></pre>
12882  * </div>
12883  * </li>
12884  * 
12885  * 
12886  * <li><b><u>Conditional processing with basic comparison operators</u></b> 
12887  * <div class="sub-desc">
12888  * <p>The <b><tt>tpl</tt></b> tag and the <b><tt>if</tt></b> operator are used
12889  * to provide conditional checks for deciding whether or not to render specific
12890  * parts of the template. Notes:<div class="sub-desc"><ul>
12891  * <li>Double quotes must be encoded if used within the conditional</li>
12892  * <li>There is no <tt>else</tt> operator &mdash; if needed, two opposite
12893  * <tt>if</tt> statements should be used.</li>
12894  * </ul></div>
12895  * <pre><code>
12896 &lt;tpl if="age &gt; 1 &amp;&amp; age &lt; 10">Child&lt;/tpl>
12897 &lt;tpl if="age >= 10 && age < 18">Teenager&lt;/tpl>
12898 &lt;tpl <b>if</b>="this.isGirl(name)">...&lt;/tpl>
12899 &lt;tpl <b>if</b>="id==\'download\'">...&lt;/tpl>
12900 &lt;tpl <b>if</b>="needsIcon">&lt;img src="{icon}" class="{iconCls}"/>&lt;/tpl>
12901 // no good:
12902 &lt;tpl if="name == "Jack"">Hello&lt;/tpl>
12903 // encode &#34; if it is part of the condition, e.g.
12904 &lt;tpl if="name == &#38;quot;Jack&#38;quot;">Hello&lt;/tpl>
12905  * </code></pre>
12906  * Using the sample data above:
12907  * <pre><code>
12908 var tpl = new Ext.XTemplate(
12909     '&lt;p>Name: {name}&lt;/p>',
12910     '&lt;p>Kids: ',
12911     '&lt;tpl for="kids">',
12912         '&lt;tpl if="age > 1">',
12913             '&lt;p>{name}&lt;/p>',
12914         '&lt;/tpl>',
12915     '&lt;/tpl>&lt;/p>'
12916 );
12917 tpl.overwrite(panel.body, data);
12918  * </code></pre>
12919  * </div>
12920  * </li>
12921  * 
12922  * 
12923  * <li><b><u>Basic math support</u></b> 
12924  * <div class="sub-desc">
12925  * <p>The following basic math operators may be applied directly on numeric
12926  * data values:</p><pre>
12927  * + - * /
12928  * </pre>
12929  * For example:
12930  * <pre><code>
12931 var tpl = new Ext.XTemplate(
12932     '&lt;p>Name: {name}&lt;/p>',
12933     '&lt;p>Kids: ',
12934     '&lt;tpl for="kids">',
12935         '&lt;tpl if="age &amp;gt; 1">',  // <-- Note that the &gt; is encoded
12936             '&lt;p>{#}: {name}&lt;/p>',  // <-- Auto-number each item
12937             '&lt;p>In 5 Years: {age+5}&lt;/p>',  // <-- Basic math
12938             '&lt;p>Dad: {parent.name}&lt;/p>',
12939         '&lt;/tpl>',
12940     '&lt;/tpl>&lt;/p>'
12941 );
12942 tpl.overwrite(panel.body, data);
12943 </code></pre>
12944  * </div>
12945  * </li>
12946  *
12947  * 
12948  * <li><b><u>Execute arbitrary inline code with special built-in template variables</u></b> 
12949  * <div class="sub-desc">
12950  * <p>Anything between <code>{[ ... ]}</code> is considered code to be executed
12951  * in the scope of the template. There are some special variables available in that code:
12952  * <ul>
12953  * <li><b><tt>values</tt></b>: The values in the current scope. If you are using
12954  * scope changing sub-templates, you can change what <tt>values</tt> is.</li>
12955  * <li><b><tt>parent</tt></b>: The scope (values) of the ancestor template.</li>
12956  * <li><b><tt>xindex</tt></b>: If you are in a looping template, the index of the
12957  * loop you are in (1-based).</li>
12958  * <li><b><tt>xcount</tt></b>: If you are in a looping template, the total length
12959  * of the array you are looping.</li>
12960  * <li><b><tt>fm</tt></b>: An alias for <tt>Ext.util.Format</tt>.</li>
12961  * </ul>
12962  * This example demonstrates basic row striping using an inline code block and the
12963  * <tt>xindex</tt> variable:</p>
12964  * <pre><code>
12965 var tpl = new Ext.XTemplate(
12966     '&lt;p>Name: {name}&lt;/p>',
12967     '&lt;p>Company: {[values.company.toUpperCase() + ", " + values.title]}&lt;/p>',
12968     '&lt;p>Kids: ',
12969     '&lt;tpl for="kids">',
12970        '&lt;div class="{[xindex % 2 === 0 ? "even" : "odd"]}">',
12971         '{name}',
12972         '&lt;/div>',
12973     '&lt;/tpl>&lt;/p>'
12974 );
12975 tpl.overwrite(panel.body, data);
12976  * </code></pre>
12977  * </div>
12978  * </li>
12979  * 
12980  * <li><b><u>Template member functions</u></b> 
12981  * <div class="sub-desc">
12982  * <p>One or more member functions can be specified in a configuration
12983  * object passed into the XTemplate constructor for more complex processing:</p>
12984  * <pre><code>
12985 var tpl = new Ext.XTemplate(
12986     '&lt;p>Name: {name}&lt;/p>',
12987     '&lt;p>Kids: ',
12988     '&lt;tpl for="kids">',
12989         '&lt;tpl if="this.isGirl(name)">',
12990             '&lt;p>Girl: {name} - {age}&lt;/p>',
12991         '&lt;/tpl>',
12992         // use opposite if statement to simulate 'else' processing:
12993         '&lt;tpl if="this.isGirl(name) == false">',
12994             '&lt;p>Boy: {name} - {age}&lt;/p>',
12995         '&lt;/tpl>',
12996         '&lt;tpl if="this.isBaby(age)">',
12997             '&lt;p>{name} is a baby!&lt;/p>',
12998         '&lt;/tpl>',
12999     '&lt;/tpl>&lt;/p>',
13000     {
13001         // XTemplate configuration:
13002         compiled: true,
13003         disableFormats: true,
13004         // member functions:
13005         isGirl: function(name){
13006             return name == 'Sara Grace';
13007         },
13008         isBaby: function(age){
13009             return age < 1;
13010         }
13011     }
13012 );
13013 tpl.overwrite(panel.body, data);
13014  * </code></pre>
13015  * </div>
13016  * </li>
13017  * 
13018  * </ul></div>
13019  * 
13020  * @param {Mixed} config
13021  */
13022 Ext.XTemplate = function(){
13023     Ext.XTemplate.superclass.constructor.apply(this, arguments);
13024
13025     var me = this,
13026         s = me.html,
13027         re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
13028         nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
13029         ifRe = /^<tpl\b[^>]*?if="(.*?)"/,
13030         execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
13031         m,
13032         id = 0,
13033         tpls = [],
13034         VALUES = 'values',
13035         PARENT = 'parent',
13036         XINDEX = 'xindex',
13037         XCOUNT = 'xcount',
13038         RETURN = 'return ',
13039         WITHVALUES = 'with(values){ ';
13040
13041     s = ['<tpl>', s, '</tpl>'].join('');
13042
13043     while((m = s.match(re))){
13044         var m2 = m[0].match(nameRe),
13045                         m3 = m[0].match(ifRe),
13046                 m4 = m[0].match(execRe),
13047                 exp = null,
13048                 fn = null,
13049                 exec = null,
13050                 name = m2 && m2[1] ? m2[1] : '';
13051
13052        if (m3) {
13053            exp = m3 && m3[1] ? m3[1] : null;
13054            if(exp){
13055                fn = new Function(VALUES, PARENT, XINDEX, XCOUNT, WITHVALUES + RETURN +(Ext.util.Format.htmlDecode(exp))+'; }');
13056            }
13057        }
13058        if (m4) {
13059            exp = m4 && m4[1] ? m4[1] : null;
13060            if(exp){
13061                exec = new Function(VALUES, PARENT, XINDEX, XCOUNT, WITHVALUES +(Ext.util.Format.htmlDecode(exp))+'; }');
13062            }
13063        }
13064        if(name){
13065            switch(name){
13066                case '.': name = new Function(VALUES, PARENT, WITHVALUES + RETURN + VALUES + '; }'); break;
13067                case '..': name = new Function(VALUES, PARENT, WITHVALUES + RETURN + PARENT + '; }'); break;
13068                default: name = new Function(VALUES, PARENT, WITHVALUES + RETURN + name + '; }');
13069            }
13070        }
13071        tpls.push({
13072             id: id,
13073             target: name,
13074             exec: exec,
13075             test: fn,
13076             body: m[1]||''
13077         });
13078        s = s.replace(m[0], '{xtpl'+ id + '}');
13079        ++id;
13080     }
13081         Ext.each(tpls, function(t) {
13082         me.compileTpl(t);
13083     });
13084     me.master = tpls[tpls.length-1];
13085     me.tpls = tpls;
13086 };
13087 Ext.extend(Ext.XTemplate, Ext.Template, {
13088     // private
13089     re : /\{([\w-\.\#]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?(\s?[\+\-\*\\]\s?[\d\.\+\-\*\\\(\)]+)?\}/g,
13090     // private
13091     codeRe : /\{\[((?:\\\]|.|\n)*?)\]\}/g,
13092
13093     // private
13094     applySubTemplate : function(id, values, parent, xindex, xcount){
13095         var me = this,
13096                 len,
13097                 t = me.tpls[id],
13098                 vs,
13099                 buf = [];
13100         if ((t.test && !t.test.call(me, values, parent, xindex, xcount)) ||
13101             (t.exec && t.exec.call(me, values, parent, xindex, xcount))) {
13102             return '';
13103         }
13104         vs = t.target ? t.target.call(me, values, parent) : values;
13105         len = vs.length;
13106         parent = t.target ? values : parent;
13107         if(t.target && Ext.isArray(vs)){
13108                 Ext.each(vs, function(v, i) {
13109                 buf[buf.length] = t.compiled.call(me, v, parent, i+1, len);
13110             });
13111             return buf.join('');
13112         }
13113         return t.compiled.call(me, vs, parent, xindex, xcount);
13114     },
13115
13116     // private
13117     compileTpl : function(tpl){
13118         var fm = Ext.util.Format,
13119                 useF = this.disableFormats !== true,
13120             sep = Ext.isGecko ? "+" : ",",
13121             body;
13122
13123         function fn(m, name, format, args, math){
13124             if(name.substr(0, 4) == 'xtpl'){
13125                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent, xindex, xcount)'+sep+"'";
13126             }
13127             var v;
13128             if(name === '.'){
13129                 v = 'values';
13130             }else if(name === '#'){
13131                 v = 'xindex';
13132             }else if(name.indexOf('.') != -1){
13133                 v = name;
13134             }else{
13135                 v = "values['" + name + "']";
13136             }
13137             if(math){
13138                 v = '(' + v + math + ')';
13139             }
13140             if (format && useF) {
13141                 args = args ? ',' + args : "";
13142                 if(format.substr(0, 5) != "this."){
13143                     format = "fm." + format + '(';
13144                 }else{
13145                     format = 'this.call("'+ format.substr(5) + '", ';
13146                     args = ", values";
13147                 }
13148             } else {
13149                 args= ''; format = "("+v+" === undefined ? '' : ";
13150             }
13151             return "'"+ sep + format + v + args + ")"+sep+"'";
13152         }
13153
13154         function codeFn(m, code){
13155             // Single quotes get escaped when the template is compiled, however we want to undo this when running code.
13156             return "'" + sep + '(' + code.replace(/\\'/g, "'") + ')' + sep + "'";
13157         }
13158
13159         // branched to use + in gecko and [].join() in others
13160         if(Ext.isGecko){
13161             body = "tpl.compiled = function(values, parent, xindex, xcount){ return '" +
13162                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn).replace(this.codeRe, codeFn) +
13163                     "';};";
13164         }else{
13165             body = ["tpl.compiled = function(values, parent, xindex, xcount){ return ['"];
13166             body.push(tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn).replace(this.codeRe, codeFn));
13167             body.push("'].join('');};");
13168             body = body.join('');
13169         }
13170         eval(body);
13171         return this;
13172     },
13173
13174     /**
13175      * Returns an HTML fragment of this template with the specified values applied.
13176      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
13177      * @return {String} The HTML fragment
13178      */
13179     applyTemplate : function(values){
13180         return this.master.compiled.call(this, values, {}, 1, 1);
13181     },
13182
13183     /**
13184      * Compile the template to a function for optimized performance.  Recommended if the template will be used frequently.
13185      * @return {Function} The compiled function
13186      */
13187     compile : function(){return this;}
13188
13189     /**
13190      * @property re
13191      * @hide
13192      */
13193     /**
13194      * @property disableFormats
13195      * @hide
13196      */
13197     /**
13198      * @method set
13199      * @hide
13200      */
13201
13202 });
13203 /**
13204  * Alias for {@link #applyTemplate}
13205  * Returns an HTML fragment of this template with the specified values applied.
13206  * @param {Object/Array} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
13207  * @return {String} The HTML fragment
13208  * @member Ext.XTemplate
13209  * @method apply
13210  */
13211 Ext.XTemplate.prototype.apply = Ext.XTemplate.prototype.applyTemplate;
13212
13213 /**
13214  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
13215  * @param {String/HTMLElement} el A DOM element or its id
13216  * @return {Ext.Template} The created template
13217  * @static
13218  */
13219 Ext.XTemplate.from = function(el){
13220     el = Ext.getDom(el);
13221     return new Ext.XTemplate(el.value || el.innerHTML);
13222 };/**\r
13223  * @class Ext.util.CSS\r
13224  * Utility class for manipulating CSS rules\r
13225  * @singleton\r
13226  */\r
13227 Ext.util.CSS = function(){\r
13228         var rules = null;\r
13229         var doc = document;\r
13230 \r
13231     var camelRe = /(-[a-z])/gi;\r
13232     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };\r
13233 \r
13234    return {\r
13235    /**\r
13236     * Creates a stylesheet from a text blob of rules.\r
13237     * These rules will be wrapped in a STYLE tag and appended to the HEAD of the document.\r
13238     * @param {String} cssText The text containing the css rules\r
13239     * @param {String} id An id to add to the stylesheet for later removal\r
13240     * @return {StyleSheet}\r
13241     */\r
13242    createStyleSheet : function(cssText, id){\r
13243        var ss;\r
13244        var head = doc.getElementsByTagName("head")[0];\r
13245        var rules = doc.createElement("style");\r
13246        rules.setAttribute("type", "text/css");\r
13247        if(id){\r
13248            rules.setAttribute("id", id);\r
13249        }\r
13250        if(Ext.isIE){\r
13251            head.appendChild(rules);\r
13252            ss = rules.styleSheet;\r
13253            ss.cssText = cssText;\r
13254        }else{\r
13255            try{\r
13256                 rules.appendChild(doc.createTextNode(cssText));\r
13257            }catch(e){\r
13258                rules.cssText = cssText;\r
13259            }\r
13260            head.appendChild(rules);\r
13261            ss = rules.styleSheet ? rules.styleSheet : (rules.sheet || doc.styleSheets[doc.styleSheets.length-1]);\r
13262        }\r
13263        this.cacheStyleSheet(ss);\r
13264        return ss;\r
13265    },\r
13266 \r
13267    /**\r
13268     * Removes a style or link tag by id\r
13269     * @param {String} id The id of the tag\r
13270     */\r
13271    removeStyleSheet : function(id){\r
13272        var existing = doc.getElementById(id);\r
13273        if(existing){\r
13274            existing.parentNode.removeChild(existing);\r
13275        }\r
13276    },\r
13277 \r
13278    /**\r
13279     * Dynamically swaps an existing stylesheet reference for a new one\r
13280     * @param {String} id The id of an existing link tag to remove\r
13281     * @param {String} url The href of the new stylesheet to include\r
13282     */\r
13283    swapStyleSheet : function(id, url){\r
13284        this.removeStyleSheet(id);\r
13285        var ss = doc.createElement("link");\r
13286        ss.setAttribute("rel", "stylesheet");\r
13287        ss.setAttribute("type", "text/css");\r
13288        ss.setAttribute("id", id);\r
13289        ss.setAttribute("href", url);\r
13290        doc.getElementsByTagName("head")[0].appendChild(ss);\r
13291    },\r
13292    \r
13293    /**\r
13294     * Refresh the rule cache if you have dynamically added stylesheets\r
13295     * @return {Object} An object (hash) of rules indexed by selector\r
13296     */\r
13297    refreshCache : function(){\r
13298        return this.getRules(true);\r
13299    },\r
13300 \r
13301    // private\r
13302    cacheStyleSheet : function(ss){\r
13303        if(!rules){\r
13304            rules = {};\r
13305        }\r
13306        try{// try catch for cross domain access issue\r
13307            var ssRules = ss.cssRules || ss.rules;\r
13308            for(var j = ssRules.length-1; j >= 0; --j){\r
13309                rules[ssRules[j].selectorText.toLowerCase()] = ssRules[j];\r
13310            }\r
13311        }catch(e){}\r
13312    },\r
13313    \r
13314    /**\r
13315     * Gets all css rules for the document\r
13316     * @param {Boolean} refreshCache true to refresh the internal cache\r
13317     * @return {Object} An object (hash) of rules indexed by selector\r
13318     */\r
13319    getRules : function(refreshCache){\r
13320                 if(rules === null || refreshCache){\r
13321                         rules = {};\r
13322                         var ds = doc.styleSheets;\r
13323                         for(var i =0, len = ds.length; i < len; i++){\r
13324                             try{\r
13325                         this.cacheStyleSheet(ds[i]);\r
13326                     }catch(e){} \r
13327                 }\r
13328                 }\r
13329                 return rules;\r
13330         },\r
13331         \r
13332         /**\r
13333     * Gets an an individual CSS rule by selector(s)\r
13334     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.\r
13335     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically\r
13336     * @return {CSSRule} The CSS rule or null if one is not found\r
13337     */\r
13338    getRule : function(selector, refreshCache){\r
13339                 var rs = this.getRules(refreshCache);\r
13340                 if(!Ext.isArray(selector)){\r
13341                     return rs[selector.toLowerCase()];\r
13342                 }\r
13343                 for(var i = 0; i < selector.length; i++){\r
13344                         if(rs[selector[i]]){\r
13345                                 return rs[selector[i].toLowerCase()];\r
13346                         }\r
13347                 }\r
13348                 return null;\r
13349         },\r
13350         \r
13351         \r
13352         /**\r
13353     * Updates a rule property\r
13354     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.\r
13355     * @param {String} property The css property\r
13356     * @param {String} value The new value for the property\r
13357     * @return {Boolean} true If a rule was found and updated\r
13358     */\r
13359    updateRule : function(selector, property, value){\r
13360                 if(!Ext.isArray(selector)){\r
13361                         var rule = this.getRule(selector);\r
13362                         if(rule){\r
13363                                 rule.style[property.replace(camelRe, camelFn)] = value;\r
13364                                 return true;\r
13365                         }\r
13366                 }else{\r
13367                         for(var i = 0; i < selector.length; i++){\r
13368                                 if(this.updateRule(selector[i], property, value)){\r
13369                                         return true;\r
13370                                 }\r
13371                         }\r
13372                 }\r
13373                 return false;\r
13374         }\r
13375    };   \r
13376 }();/**
13377  @class Ext.util.ClickRepeater
13378  @extends Ext.util.Observable
13379
13380  A wrapper class which can be applied to any element. Fires a "click" event while the
13381  mouse is pressed. The interval between firings may be specified in the config but
13382  defaults to 20 milliseconds.
13383
13384  Optionally, a CSS class may be applied to the element during the time it is pressed.
13385
13386  @cfg {Mixed} el The element to act as a button.
13387  @cfg {Number} delay The initial delay before the repeating event begins firing.
13388  Similar to an autorepeat key delay.
13389  @cfg {Number} interval The interval between firings of the "click" event. Default 20 ms.
13390  @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
13391  @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
13392            "interval" and "delay" are ignored.
13393  @cfg {Boolean} preventDefault True to prevent the default click event
13394  @cfg {Boolean} stopDefault True to stop the default click event
13395
13396  @history
13397     2007-02-02 jvs Original code contributed by Nige "Animal" White
13398     2007-02-02 jvs Renamed to ClickRepeater
13399     2007-02-03 jvs Modifications for FF Mac and Safari
13400
13401  @constructor
13402  @param {Mixed} el The element to listen on
13403  @param {Object} config
13404  */
13405 Ext.util.ClickRepeater = function(el, config)
13406 {
13407     this.el = Ext.get(el);
13408     this.el.unselectable();
13409
13410     Ext.apply(this, config);
13411
13412     this.addEvents(
13413     /**
13414      * @event mousedown
13415      * Fires when the mouse button is depressed.
13416      * @param {Ext.util.ClickRepeater} this
13417      */
13418         "mousedown",
13419     /**
13420      * @event click
13421      * Fires on a specified interval during the time the element is pressed.
13422      * @param {Ext.util.ClickRepeater} this
13423      */
13424         "click",
13425     /**
13426      * @event mouseup
13427      * Fires when the mouse key is released.
13428      * @param {Ext.util.ClickRepeater} this
13429      */
13430         "mouseup"
13431     );
13432
13433     if(!this.disabled){
13434         this.disabled = true;
13435         this.enable();
13436     }
13437
13438     // allow inline handler
13439     if(this.handler){
13440         this.on("click", this.handler,  this.scope || this);
13441     }
13442
13443     Ext.util.ClickRepeater.superclass.constructor.call(this);
13444 };
13445
13446 Ext.extend(Ext.util.ClickRepeater, Ext.util.Observable, {
13447     interval : 20,
13448     delay: 250,
13449     preventDefault : true,
13450     stopDefault : false,
13451     timer : 0,
13452
13453     /**
13454      * Enables the repeater and allows events to fire.
13455      */
13456     enable: function(){
13457         if(this.disabled){
13458             this.el.on('mousedown', this.handleMouseDown, this);
13459             if(this.preventDefault || this.stopDefault){
13460                 this.el.on('click', this.eventOptions, this);
13461             }
13462         }
13463         this.disabled = false;
13464     },
13465     
13466     /**
13467      * Disables the repeater and stops events from firing.
13468      */
13469     disable: function(/* private */ force){
13470         if(force || !this.disabled){
13471             clearTimeout(this.timer);
13472             if(this.pressClass){
13473                 this.el.removeClass(this.pressClass);
13474             }
13475             Ext.getDoc().un('mouseup', this.handleMouseUp, this);
13476             this.el.removeAllListeners();
13477         }
13478         this.disabled = true;
13479     },
13480     
13481     /**
13482      * Convenience function for setting disabled/enabled by boolean.
13483      * @param {Boolean} disabled
13484      */
13485     setDisabled: function(disabled){
13486         this[disabled ? 'disable' : 'enable']();    
13487     },
13488     
13489     eventOptions: function(e){
13490         if(this.preventDefault){
13491             e.preventDefault();
13492         }
13493         if(this.stopDefault){
13494             e.stopEvent();
13495         }       
13496     },
13497     
13498     // private
13499     destroy : function() {
13500         this.disable(true);
13501         Ext.destroy(this.el);
13502         this.purgeListeners();
13503     },
13504     
13505     // private
13506     handleMouseDown : function(){
13507         clearTimeout(this.timer);
13508         this.el.blur();
13509         if(this.pressClass){
13510             this.el.addClass(this.pressClass);
13511         }
13512         this.mousedownTime = new Date();
13513
13514         Ext.getDoc().on("mouseup", this.handleMouseUp, this);
13515         this.el.on("mouseout", this.handleMouseOut, this);
13516
13517         this.fireEvent("mousedown", this);
13518         this.fireEvent("click", this);
13519
13520 //      Do not honor delay or interval if acceleration wanted.
13521         if (this.accelerate) {
13522             this.delay = 400;
13523             }
13524         this.timer = this.click.defer(this.delay || this.interval, this);
13525     },
13526
13527     // private
13528     click : function(){
13529         this.fireEvent("click", this);
13530         this.timer = this.click.defer(this.accelerate ?
13531             this.easeOutExpo(this.mousedownTime.getElapsed(),
13532                 400,
13533                 -390,
13534                 12000) :
13535             this.interval, this);
13536     },
13537
13538     easeOutExpo : function (t, b, c, d) {
13539         return (t==d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b;
13540     },
13541
13542     // private
13543     handleMouseOut : function(){
13544         clearTimeout(this.timer);
13545         if(this.pressClass){
13546             this.el.removeClass(this.pressClass);
13547         }
13548         this.el.on("mouseover", this.handleMouseReturn, this);
13549     },
13550
13551     // private
13552     handleMouseReturn : function(){
13553         this.el.un("mouseover", this.handleMouseReturn, this);
13554         if(this.pressClass){
13555             this.el.addClass(this.pressClass);
13556         }
13557         this.click();
13558     },
13559
13560     // private
13561     handleMouseUp : function(){
13562         clearTimeout(this.timer);
13563         this.el.un("mouseover", this.handleMouseReturn, this);
13564         this.el.un("mouseout", this.handleMouseOut, this);
13565         Ext.getDoc().un("mouseup", this.handleMouseUp, this);
13566         this.el.removeClass(this.pressClass);
13567         this.fireEvent("mouseup", this);
13568     }
13569 });/**
13570  * @class Ext.KeyNav
13571  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
13572  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
13573  * way to implement custom navigation schemes for any UI component.</p>
13574  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
13575  * pageUp, pageDown, del, home, end.  Usage:</p>
13576  <pre><code>
13577 var nav = new Ext.KeyNav("my-element", {
13578     "left" : function(e){
13579         this.moveLeft(e.ctrlKey);
13580     },
13581     "right" : function(e){
13582         this.moveRight(e.ctrlKey);
13583     },
13584     "enter" : function(e){
13585         this.save();
13586     },
13587     scope : this
13588 });
13589 </code></pre>
13590  * @constructor
13591  * @param {Mixed} el The element to bind to
13592  * @param {Object} config The config
13593  */
13594 Ext.KeyNav = function(el, config){
13595     this.el = Ext.get(el);
13596     Ext.apply(this, config);
13597     if(!this.disabled){
13598         this.disabled = true;
13599         this.enable();
13600     }
13601 };
13602
13603 Ext.KeyNav.prototype = {
13604     /**
13605      * @cfg {Boolean} disabled
13606      * True to disable this KeyNav instance (defaults to false)
13607      */
13608     disabled : false,
13609     /**
13610      * @cfg {String} defaultEventAction
13611      * The method to call on the {@link Ext.EventObject} after this KeyNav intercepts a key.  Valid values are
13612      * {@link Ext.EventObject#stopEvent}, {@link Ext.EventObject#preventDefault} and
13613      * {@link Ext.EventObject#stopPropagation} (defaults to 'stopEvent')
13614      */
13615     defaultEventAction: "stopEvent",
13616     /**
13617      * @cfg {Boolean} forceKeyDown
13618      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
13619      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
13620      * handle keydown instead of keypress.
13621      */
13622     forceKeyDown : false,
13623
13624     // private
13625     relay : function(e){
13626         var k = e.getKey();
13627         var h = this.keyToHandler[k];
13628         if(h && this[h]){
13629             if(this.doRelay(e, this[h], h) !== true){
13630                 e[this.defaultEventAction]();
13631             }
13632         }
13633     },
13634
13635     // private
13636     doRelay : function(e, h, hname){
13637         return h.call(this.scope || this, e);
13638     },
13639
13640     // possible handlers
13641     enter : false,
13642     left : false,
13643     right : false,
13644     up : false,
13645     down : false,
13646     tab : false,
13647     esc : false,
13648     pageUp : false,
13649     pageDown : false,
13650     del : false,
13651     home : false,
13652     end : false,
13653
13654     // quick lookup hash
13655     keyToHandler : {
13656         37 : "left",
13657         39 : "right",
13658         38 : "up",
13659         40 : "down",
13660         33 : "pageUp",
13661         34 : "pageDown",
13662         46 : "del",
13663         36 : "home",
13664         35 : "end",
13665         13 : "enter",
13666         27 : "esc",
13667         9  : "tab"
13668     },
13669     
13670     stopKeyUp: function(e) {
13671         var k = e.getKey();
13672
13673         if (k >= 37 && k <= 40) {
13674             // *** bugfix - safari 2.x fires 2 keyup events on cursor keys
13675             // *** (note: this bugfix sacrifices the "keyup" event originating from keyNav elements in Safari 2)
13676             e.stopEvent();
13677         }
13678     },
13679     
13680     /**
13681      * Destroy this KeyNav (this is the same as calling disable).
13682      */
13683     destroy: function(){
13684         this.disable();    
13685     },
13686
13687         /**
13688          * Enable this KeyNav
13689          */
13690         enable: function() {
13691         if (this.disabled) {
13692             if (Ext.isSafari2) {
13693                 // call stopKeyUp() on "keyup" event
13694                 this.el.on('keyup', this.stopKeyUp, this);
13695             }
13696
13697             this.el.on(this.isKeydown()? 'keydown' : 'keypress', this.relay, this);
13698             this.disabled = false;
13699         }
13700     },
13701
13702         /**
13703          * Disable this KeyNav
13704          */
13705         disable: function() {
13706         if (!this.disabled) {
13707             if (Ext.isSafari2) {
13708                 // remove "keyup" event handler
13709                 this.el.un('keyup', this.stopKeyUp, this);
13710             }
13711
13712             this.el.un(this.isKeydown()? 'keydown' : 'keypress', this.relay, this);
13713             this.disabled = true;
13714         }
13715     },
13716     
13717     /**
13718      * Convenience function for setting disabled/enabled by boolean.
13719      * @param {Boolean} disabled
13720      */
13721     setDisabled : function(disabled){
13722         this[disabled ? "disable" : "enable"]();
13723     },
13724     
13725     // private
13726     isKeydown: function(){
13727         return this.forceKeyDown || Ext.EventManager.useKeydown;
13728     }
13729 };
13730 /**\r
13731  * @class Ext.KeyMap\r
13732  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.\r
13733  * The constructor accepts the same config object as defined by {@link #addBinding}.\r
13734  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key\r
13735  * combination it will call the function with this signature (if the match is a multi-key\r
13736  * combination the callback will still be called only once): (String key, Ext.EventObject e)\r
13737  * A KeyMap can also handle a string representation of keys.<br />\r
13738  * Usage:\r
13739  <pre><code>\r
13740 // map one key by key code\r
13741 var map = new Ext.KeyMap("my-element", {\r
13742     key: 13, // or Ext.EventObject.ENTER\r
13743     fn: myHandler,\r
13744     scope: myObject\r
13745 });\r
13746 \r
13747 // map multiple keys to one action by string\r
13748 var map = new Ext.KeyMap("my-element", {\r
13749     key: "a\r\n\t",\r
13750     fn: myHandler,\r
13751     scope: myObject\r
13752 });\r
13753 \r
13754 // map multiple keys to multiple actions by strings and array of codes\r
13755 var map = new Ext.KeyMap("my-element", [\r
13756     {\r
13757         key: [10,13],\r
13758         fn: function(){ alert("Return was pressed"); }\r
13759     }, {\r
13760         key: "abc",\r
13761         fn: function(){ alert('a, b or c was pressed'); }\r
13762     }, {\r
13763         key: "\t",\r
13764         ctrl:true,\r
13765         shift:true,\r
13766         fn: function(){ alert('Control + shift + tab was pressed.'); }\r
13767     }\r
13768 ]);\r
13769 </code></pre>\r
13770  * <b>Note: A KeyMap starts enabled</b>\r
13771  * @constructor\r
13772  * @param {Mixed} el The element to bind to\r
13773  * @param {Object} config The config (see {@link #addBinding})\r
13774  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")\r
13775  */\r
13776 Ext.KeyMap = function(el, config, eventName){\r
13777     this.el  = Ext.get(el);\r
13778     this.eventName = eventName || "keydown";\r
13779     this.bindings = [];\r
13780     if(config){\r
13781         this.addBinding(config);\r
13782     }\r
13783     this.enable();\r
13784 };\r
13785 \r
13786 Ext.KeyMap.prototype = {\r
13787     /**\r
13788      * True to stop the event from bubbling and prevent the default browser action if the\r
13789      * key was handled by the KeyMap (defaults to false)\r
13790      * @type Boolean\r
13791      */\r
13792     stopEvent : false,\r
13793 \r
13794     /**\r
13795      * Add a new binding to this KeyMap. The following config object properties are supported:\r
13796      * <pre>\r
13797 Property    Type             Description\r
13798 ----------  ---------------  ----------------------------------------------------------------------\r
13799 key         String/Array     A single keycode or an array of keycodes to handle\r
13800 shift       Boolean          True to handle key only when shift is pressed, False to handle the key only when shift is not pressed (defaults to undefined)\r
13801 ctrl        Boolean          True to handle key only when ctrl is pressed, False to handle the key only when ctrl is not pressed (defaults to undefined)\r
13802 alt         Boolean          True to handle key only when alt is pressed, False to handle the key only when alt is not pressed (defaults to undefined)\r
13803 handler     Function         The function to call when KeyMap finds the expected key combination\r
13804 fn          Function         Alias of handler (for backwards-compatibility)\r
13805 scope       Object           The scope of the callback function\r
13806 stopEvent   Boolean          True to stop the event from bubbling and prevent the default browser action if the key was handled by the KeyMap (defaults to false)\r
13807 </pre>\r
13808      *\r
13809      * Usage:\r
13810      * <pre><code>\r
13811 // Create a KeyMap\r
13812 var map = new Ext.KeyMap(document, {\r
13813     key: Ext.EventObject.ENTER,\r
13814     fn: handleKey,\r
13815     scope: this\r
13816 });\r
13817 \r
13818 //Add a new binding to the existing KeyMap later\r
13819 map.addBinding({\r
13820     key: 'abc',\r
13821     shift: true,\r
13822     fn: handleKey,\r
13823     scope: this\r
13824 });\r
13825 </code></pre>\r
13826      * @param {Object/Array} config A single KeyMap config or an array of configs\r
13827      */\r
13828         addBinding : function(config){\r
13829         if(Ext.isArray(config)){\r
13830             Ext.each(config, function(c){\r
13831                 this.addBinding(c);\r
13832             }, this);\r
13833             return;\r
13834         }\r
13835         var keyCode = config.key,\r
13836             fn = config.fn || config.handler,\r
13837             scope = config.scope;\r
13838 \r
13839         if (config.stopEvent) {\r
13840             this.stopEvent = config.stopEvent;    \r
13841         }       \r
13842 \r
13843         if(typeof keyCode == "string"){\r
13844             var ks = [];\r
13845             var keyString = keyCode.toUpperCase();\r
13846             for(var j = 0, len = keyString.length; j < len; j++){\r
13847                 ks.push(keyString.charCodeAt(j));\r
13848             }\r
13849             keyCode = ks;\r
13850         }\r
13851         var keyArray = Ext.isArray(keyCode);\r
13852         \r
13853         var handler = function(e){\r
13854             if(this.checkModifiers(config, e)){\r
13855                 var k = e.getKey();\r
13856                 if(keyArray){\r
13857                     for(var i = 0, len = keyCode.length; i < len; i++){\r
13858                         if(keyCode[i] == k){\r
13859                           if(this.stopEvent){\r
13860                               e.stopEvent();\r
13861                           }\r
13862                           fn.call(scope || window, k, e);\r
13863                           return;\r
13864                         }\r
13865                     }\r
13866                 }else{\r
13867                     if(k == keyCode){\r
13868                         if(this.stopEvent){\r
13869                            e.stopEvent();\r
13870                         }\r
13871                         fn.call(scope || window, k, e);\r
13872                     }\r
13873                 }\r
13874             }\r
13875         };\r
13876         this.bindings.push(handler);\r
13877         },\r
13878     \r
13879     // private\r
13880     checkModifiers: function(config, e){\r
13881         var val, key, keys = ['shift', 'ctrl', 'alt'];\r
13882         for (var i = 0, len = keys.length; i < len; ++i){\r
13883             key = keys[i];\r
13884             val = config[key];\r
13885             if(!(val === undefined || (val === e[key + 'Key']))){\r
13886                 return false;\r
13887             }\r
13888         }\r
13889         return true;\r
13890     },\r
13891 \r
13892     /**\r
13893      * Shorthand for adding a single key listener\r
13894      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the\r
13895      * following options:\r
13896      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}\r
13897      * @param {Function} fn The function to call\r
13898      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the browser window.\r
13899      */\r
13900     on : function(key, fn, scope){\r
13901         var keyCode, shift, ctrl, alt;\r
13902         if(typeof key == "object" && !Ext.isArray(key)){\r
13903             keyCode = key.key;\r
13904             shift = key.shift;\r
13905             ctrl = key.ctrl;\r
13906             alt = key.alt;\r
13907         }else{\r
13908             keyCode = key;\r
13909         }\r
13910         this.addBinding({\r
13911             key: keyCode,\r
13912             shift: shift,\r
13913             ctrl: ctrl,\r
13914             alt: alt,\r
13915             fn: fn,\r
13916             scope: scope\r
13917         });\r
13918     },\r
13919 \r
13920     // private\r
13921     handleKeyDown : function(e){\r
13922             if(this.enabled){ //just in case\r
13923             var b = this.bindings;\r
13924             for(var i = 0, len = b.length; i < len; i++){\r
13925                 b[i].call(this, e);\r
13926             }\r
13927             }\r
13928         },\r
13929 \r
13930         /**\r
13931          * Returns true if this KeyMap is enabled\r
13932          * @return {Boolean}\r
13933          */\r
13934         isEnabled : function(){\r
13935             return this.enabled;\r
13936         },\r
13937 \r
13938         /**\r
13939          * Enables this KeyMap\r
13940          */\r
13941         enable: function(){\r
13942                 if(!this.enabled){\r
13943                     this.el.on(this.eventName, this.handleKeyDown, this);\r
13944                     this.enabled = true;\r
13945                 }\r
13946         },\r
13947 \r
13948         /**\r
13949          * Disable this KeyMap\r
13950          */\r
13951         disable: function(){\r
13952                 if(this.enabled){\r
13953                     this.el.removeListener(this.eventName, this.handleKeyDown, this);\r
13954                     this.enabled = false;\r
13955                 }\r
13956         },\r
13957     \r
13958     /**\r
13959      * Convenience function for setting disabled/enabled by boolean.\r
13960      * @param {Boolean} disabled\r
13961      */\r
13962     setDisabled : function(disabled){\r
13963         this[disabled ? "disable" : "enable"]();\r
13964     }\r
13965 };/**
13966  * @class Ext.util.TextMetrics
13967  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
13968  * wide, in pixels, a given block of text will be. Note that when measuring text, it should be plain text and
13969  * should not contain any HTML, otherwise it may not be measured correctly.
13970  * @singleton
13971  */
13972 Ext.util.TextMetrics = function(){
13973     var shared;
13974     return {
13975         /**
13976          * Measures the size of the specified text
13977          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
13978          * that can affect the size of the rendered text
13979          * @param {String} text The text to measure
13980          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
13981          * in order to accurately measure the text height
13982          * @return {Object} An object containing the text's size {width: (width), height: (height)}
13983          */
13984         measure : function(el, text, fixedWidth){
13985             if(!shared){
13986                 shared = Ext.util.TextMetrics.Instance(el, fixedWidth);
13987             }
13988             shared.bind(el);
13989             shared.setFixedWidth(fixedWidth || 'auto');
13990             return shared.getSize(text);
13991         },
13992
13993         /**
13994          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
13995          * the overhead of multiple calls to initialize the style properties on each measurement.
13996          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
13997          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
13998          * in order to accurately measure the text height
13999          * @return {Ext.util.TextMetrics.Instance} instance The new instance
14000          */
14001         createInstance : function(el, fixedWidth){
14002             return Ext.util.TextMetrics.Instance(el, fixedWidth);
14003         }
14004     };
14005 }();
14006
14007 Ext.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14008     var ml = new Ext.Element(document.createElement('div'));
14009     document.body.appendChild(ml.dom);
14010     ml.position('absolute');
14011     ml.setLeftTop(-1000, -1000);
14012     ml.hide();
14013
14014     if(fixedWidth){
14015         ml.setWidth(fixedWidth);
14016     }
14017
14018     var instance = {
14019         /**
14020          * <p><b>Only available on the instance returned from {@link #createInstance}, <u>not</u> on the singleton.</b></p>
14021          * Returns the size of the specified text based on the internal element's style and width properties
14022          * @param {String} text The text to measure
14023          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14024          */
14025         getSize : function(text){
14026             ml.update(text);
14027             var s = ml.getSize();
14028             ml.update('');
14029             return s;
14030         },
14031
14032         /**
14033          * <p><b>Only available on the instance returned from {@link #createInstance}, <u>not</u> on the singleton.</b></p>
14034          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14035          * that can affect the size of the rendered text
14036          * @param {String/HTMLElement} el The element, dom node or id
14037          */
14038         bind : function(el){
14039             ml.setStyle(
14040                 Ext.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height', 'text-transform', 'letter-spacing')
14041             );
14042         },
14043
14044         /**
14045          * <p><b>Only available on the instance returned from {@link #createInstance}, <u>not</u> on the singleton.</b></p>
14046          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14047          * to set a fixed width in order to accurately measure the text height.
14048          * @param {Number} width The width to set on the element
14049          */
14050         setFixedWidth : function(width){
14051             ml.setWidth(width);
14052         },
14053
14054         /**
14055          * <p><b>Only available on the instance returned from {@link #createInstance}, <u>not</u> on the singleton.</b></p>
14056          * Returns the measured width of the specified text
14057          * @param {String} text The text to measure
14058          * @return {Number} width The width in pixels
14059          */
14060         getWidth : function(text){
14061             ml.dom.style.width = 'auto';
14062             return this.getSize(text).width;
14063         },
14064
14065         /**
14066          * <p><b>Only available on the instance returned from {@link #createInstance}, <u>not</u> on the singleton.</b></p>
14067          * Returns the measured height of the specified text.  For multiline text, be sure to call
14068          * {@link #setFixedWidth} if necessary.
14069          * @param {String} text The text to measure
14070          * @return {Number} height The height in pixels
14071          */
14072         getHeight : function(text){
14073             return this.getSize(text).height;
14074         }
14075     };
14076
14077     instance.bind(bindTo);
14078
14079     return instance;
14080 };
14081
14082 Ext.Element.addMethods({
14083     /**
14084      * Returns the width in pixels of the passed text, or the width of the text in this Element.
14085      * @param {String} text The text to measure. Defaults to the innerHTML of the element.
14086      * @param {Number} min (Optional) The minumum value to return.
14087      * @param {Number} max (Optional) The maximum value to return.
14088      * @return {Number} The text width in pixels.
14089      * @member Ext.Element getTextWidth
14090      */
14091     getTextWidth : function(text, min, max){
14092         return (Ext.util.TextMetrics.measure(this.dom, Ext.value(text, this.dom.innerHTML, true)).width).constrain(min || 0, max || 1000000);
14093     }
14094 });
14095 /**\r
14096  * @class Ext.util.Cookies\r
14097  * Utility class for managing and interacting with cookies.\r
14098  * @singleton\r
14099  */\r
14100 Ext.util.Cookies = {\r
14101     /**\r
14102      * Create a cookie with the specified name and value. Additional settings\r
14103      * for the cookie may be optionally specified (for example: expiration,\r
14104      * access restriction, SSL).\r
14105      * @param {String} name The name of the cookie to set. \r
14106      * @param {Mixed} value The value to set for the cookie.\r
14107      * @param {Object} expires (Optional) Specify an expiration date the\r
14108      * cookie is to persist until.  Note that the specified Date object will\r
14109      * be converted to Greenwich Mean Time (GMT). \r
14110      * @param {String} path (Optional) Setting a path on the cookie restricts\r
14111      * access to pages that match that path. Defaults to all pages (<tt>'/'</tt>). \r
14112      * @param {String} domain (Optional) Setting a domain restricts access to\r
14113      * pages on a given domain (typically used to allow cookie access across\r
14114      * subdomains). For example, "extjs.com" will create a cookie that can be\r
14115      * accessed from any subdomain of extjs.com, including www.extjs.com,\r
14116      * support.extjs.com, etc.\r
14117      * @param {Boolean} secure (Optional) Specify true to indicate that the cookie\r
14118      * should only be accessible via SSL on a page using the HTTPS protocol.\r
14119      * Defaults to <tt>false</tt>. Note that this will only work if the page\r
14120      * calling this code uses the HTTPS protocol, otherwise the cookie will be\r
14121      * created with default options.\r
14122      */\r
14123     set : function(name, value){\r
14124         var argv = arguments;\r
14125         var argc = arguments.length;\r
14126         var expires = (argc > 2) ? argv[2] : null;\r
14127         var path = (argc > 3) ? argv[3] : '/';\r
14128         var domain = (argc > 4) ? argv[4] : null;\r
14129         var secure = (argc > 5) ? argv[5] : false;\r
14130         document.cookie = name + "=" + escape(value) + ((expires === null) ? "" : ("; expires=" + expires.toGMTString())) + ((path === null) ? "" : ("; path=" + path)) + ((domain === null) ? "" : ("; domain=" + domain)) + ((secure === true) ? "; secure" : "");\r
14131     },\r
14132 \r
14133     /**\r
14134      * Retrieves cookies that are accessible by the current page. If a cookie\r
14135      * does not exist, <code>get()</code> returns <tt>null</tt>.  The following\r
14136      * example retrieves the cookie called "valid" and stores the String value\r
14137      * in the variable <tt>validStatus</tt>.\r
14138      * <pre><code>\r
14139      * var validStatus = Ext.util.Cookies.get("valid");\r
14140      * </code></pre>\r
14141      * @param {String} name The name of the cookie to get\r
14142      * @return {Mixed} Returns the cookie value for the specified name;\r
14143      * null if the cookie name does not exist.\r
14144      */\r
14145     get : function(name){\r
14146         var arg = name + "=";\r
14147         var alen = arg.length;\r
14148         var clen = document.cookie.length;\r
14149         var i = 0;\r
14150         var j = 0;\r
14151         while(i < clen){\r
14152             j = i + alen;\r
14153             if(document.cookie.substring(i, j) == arg){\r
14154                 return Ext.util.Cookies.getCookieVal(j);\r
14155             }\r
14156             i = document.cookie.indexOf(" ", i) + 1;\r
14157             if(i === 0){\r
14158                 break;\r
14159             }\r
14160         }\r
14161         return null;\r
14162     },\r
14163 \r
14164     /**\r
14165      * Removes a cookie with the provided name from the browser\r
14166      * if found by setting its expiration date to sometime in the past. \r
14167      * @param {String} name The name of the cookie to remove\r
14168      */\r
14169     clear : function(name){\r
14170         if(Ext.util.Cookies.get(name)){\r
14171             document.cookie = name + "=" + "; expires=Thu, 01-Jan-70 00:00:01 GMT";\r
14172         }\r
14173     },\r
14174     /**\r
14175      * @private\r
14176      */\r
14177     getCookieVal : function(offset){\r
14178         var endstr = document.cookie.indexOf(";", offset);\r
14179         if(endstr == -1){\r
14180             endstr = document.cookie.length;\r
14181         }\r
14182         return unescape(document.cookie.substring(offset, endstr));\r
14183     }\r
14184 };/**
14185  * Framework-wide error-handler.  Developers can override this method to provide
14186  * custom exception-handling.  Framework errors will often extend from the base
14187  * Ext.Error class.
14188  * @param {Object/Error} e The thrown exception object.
14189  */
14190 Ext.handleError = function(e) {
14191     throw e;
14192 };
14193
14194 /**
14195  * @class Ext.Error
14196  * @extends Error
14197  * <p>A base error class. Future implementations are intended to provide more
14198  * robust error handling throughout the framework (<b>in the debug build only</b>)
14199  * to check for common errors and problems. The messages issued by this class
14200  * will aid error checking. Error checks will be automatically removed in the
14201  * production build so that performance is not negatively impacted.</p>
14202  * <p>Some sample messages currently implemented:</p><pre>
14203 "DataProxy attempted to execute an API-action but found an undefined
14204 url / function. Please review your Proxy url/api-configuration."
14205  * </pre><pre>
14206 "Could not locate your "root" property in your server response.
14207 Please review your JsonReader config to ensure the config-property
14208 "root" matches the property your server-response.  See the JsonReader
14209 docs for additional assistance."
14210  * </pre>
14211  * <p>An example of the code used for generating error messages:</p><pre><code>
14212 try {
14213     generateError({
14214         foo: 'bar'
14215     });
14216 }
14217 catch (e) {
14218     console.error(e);
14219 }
14220 function generateError(data) {
14221     throw new Ext.Error('foo-error', data);
14222 }
14223  * </code></pre>
14224  * @param {String} message
14225  */
14226 Ext.Error = function(message) {
14227     // Try to read the message from Ext.Error.lang
14228     this.message = (this.lang[message]) ? this.lang[message] : message;
14229 }
14230 Ext.Error.prototype = new Error();
14231 Ext.apply(Ext.Error.prototype, {
14232     // protected.  Extensions place their error-strings here.
14233     lang: {},
14234
14235     name: 'Ext.Error',
14236     /**
14237      * getName
14238      * @return {String}
14239      */
14240     getName : function() {
14241         return this.name;
14242     },
14243     /**
14244      * getMessage
14245      * @return {String}
14246      */
14247     getMessage : function() {
14248         return this.message;
14249     },
14250     /**
14251      * toJson
14252      * @return {String}
14253      */
14254     toJson : function() {
14255         return Ext.encode(this);
14256     }
14257 });
14258
14259 /**
14260  * @class Ext.ComponentMgr
14261  * <p>Provides a registry of all Components (instances of {@link Ext.Component} or any subclass
14262  * thereof) on a page so that they can be easily accessed by {@link Ext.Component component}
14263  * {@link Ext.Component#id id} (see {@link #get}, or the convenience method {@link Ext#getCmp Ext.getCmp}).</p>
14264  * <p>This object also provides a registry of available Component <i>classes</i>
14265  * indexed by a mnemonic code known as the Component's {@link Ext.Component#xtype xtype}.
14266  * The <code>{@link Ext.Component#xtype xtype}</code> provides a way to avoid instantiating child Components
14267  * when creating a full, nested config object for a complete Ext page.</p>
14268  * <p>A child Component may be specified simply as a <i>config object</i>
14269  * as long as the correct <code>{@link Ext.Component#xtype xtype}</code> is specified so that if and when the Component
14270  * needs rendering, the correct type can be looked up for lazy instantiation.</p>
14271  * <p>For a list of all available <code>{@link Ext.Component#xtype xtypes}</code>, see {@link Ext.Component}.</p>
14272  * @singleton
14273  */
14274 Ext.ComponentMgr = function(){
14275     var all = new Ext.util.MixedCollection();
14276     var types = {};
14277     var ptypes = {};
14278
14279     return {
14280         /**
14281          * Registers a component.
14282          * @param {Ext.Component} c The component
14283          */
14284         register : function(c){
14285             all.add(c);
14286         },
14287
14288         /**
14289          * Unregisters a component.
14290          * @param {Ext.Component} c The component
14291          */
14292         unregister : function(c){
14293             all.remove(c);
14294         },
14295
14296         /**
14297          * Returns a component by {@link Ext.Component#id id}.
14298          * For additional details see {@link Ext.util.MixedCollection#get}.
14299          * @param {String} id The component {@link Ext.Component#id id}
14300          * @return Ext.Component The Component, <code>undefined</code> if not found, or <code>null</code> if a
14301          * Class was found.
14302          */
14303         get : function(id){
14304             return all.get(id);
14305         },
14306
14307         /**
14308          * Registers a function that will be called when a Component with the specified id is added to ComponentMgr. This will happen on instantiation.
14309          * @param {String} id The component {@link Ext.Component#id id}
14310          * @param {Function} fn The callback function
14311          * @param {Object} scope The scope (<code>this</code> reference) in which the callback is executed. Defaults to the Component.
14312          */
14313         onAvailable : function(id, fn, scope){
14314             all.on("add", function(index, o){
14315                 if(o.id == id){
14316                     fn.call(scope || o, o);
14317                     all.un("add", fn, scope);
14318                 }
14319             });
14320         },
14321
14322         /**
14323          * The MixedCollection used internally for the component cache. An example usage may be subscribing to
14324          * events on the MixedCollection to monitor addition or removal.  Read-only.
14325          * @type {MixedCollection}
14326          */
14327         all : all,
14328         
14329         /**
14330          * The xtypes that have been registered with the component manager.
14331          * @type {Object}
14332          */
14333         types : types,
14334         
14335         /**
14336          * The ptypes that have been registered with the component manager.
14337          * @type {Object}
14338          */
14339         ptypes: ptypes,
14340         
14341         /**
14342          * Checks if a Component type is registered.
14343          * @param {Ext.Component} xtype The mnemonic string by which the Component class may be looked up
14344          * @return {Boolean} Whether the type is registered.
14345          */
14346         isRegistered : function(xtype){
14347             return types[xtype] !== undefined;    
14348         },
14349         
14350         /**
14351          * Checks if a Plugin type is registered.
14352          * @param {Ext.Component} ptype The mnemonic string by which the Plugin class may be looked up
14353          * @return {Boolean} Whether the type is registered.
14354          */
14355         isPluginRegistered : function(ptype){
14356             return ptypes[ptype] !== undefined;    
14357         },        
14358
14359         /**
14360          * <p>Registers a new Component constructor, keyed by a new
14361          * {@link Ext.Component#xtype}.</p>
14362          * <p>Use this method (or its alias {@link Ext#reg Ext.reg}) to register new
14363          * subclasses of {@link Ext.Component} so that lazy instantiation may be used when specifying
14364          * child Components.
14365          * see {@link Ext.Container#items}</p>
14366          * @param {String} xtype The mnemonic string by which the Component class may be looked up.
14367          * @param {Constructor} cls The new Component class.
14368          */
14369         registerType : function(xtype, cls){
14370             types[xtype] = cls;
14371             cls.xtype = xtype;
14372         },
14373
14374         /**
14375          * Creates a new Component from the specified config object using the
14376          * config object's {@link Ext.component#xtype xtype} to determine the class to instantiate.
14377          * @param {Object} config A configuration object for the Component you wish to create.
14378          * @param {Constructor} defaultType The constructor to provide the default Component type if
14379          * the config object does not contain a <code>xtype</code>. (Optional if the config contains a <code>xtype</code>).
14380          * @return {Ext.Component} The newly instantiated Component.
14381          */
14382         create : function(config, defaultType){
14383             return config.render ? config : new types[config.xtype || defaultType](config);
14384         },
14385
14386         /**
14387          * <p>Registers a new Plugin constructor, keyed by a new
14388          * {@link Ext.Component#ptype}.</p>
14389          * <p>Use this method (or its alias {@link Ext#preg Ext.preg}) to register new
14390          * plugins for {@link Ext.Component}s so that lazy instantiation may be used when specifying
14391          * Plugins.</p>
14392          * @param {String} ptype The mnemonic string by which the Plugin class may be looked up.
14393          * @param {Constructor} cls The new Plugin class.
14394          */
14395         registerPlugin : function(ptype, cls){
14396             ptypes[ptype] = cls;
14397             cls.ptype = ptype;
14398         },
14399
14400         /**
14401          * Creates a new Plugin from the specified config object using the
14402          * config object's {@link Ext.component#ptype ptype} to determine the class to instantiate.
14403          * @param {Object} config A configuration object for the Plugin you wish to create.
14404          * @param {Constructor} defaultType The constructor to provide the default Plugin type if
14405          * the config object does not contain a <code>ptype</code>. (Optional if the config contains a <code>ptype</code>).
14406          * @return {Ext.Component} The newly instantiated Plugin.
14407          */
14408         createPlugin : function(config, defaultType){
14409             var PluginCls = ptypes[config.ptype || defaultType];
14410             if (PluginCls.init) {
14411                 return PluginCls;                
14412             } else {
14413                 return new PluginCls(config);
14414             }            
14415         }
14416     };
14417 }();
14418
14419 /**
14420  * Shorthand for {@link Ext.ComponentMgr#registerType}
14421  * @param {String} xtype The {@link Ext.component#xtype mnemonic string} by which the Component class
14422  * may be looked up.
14423  * @param {Constructor} cls The new Component class.
14424  * @member Ext
14425  * @method reg
14426  */
14427 Ext.reg = Ext.ComponentMgr.registerType; // this will be called a lot internally, shorthand to keep the bytes down
14428 /**
14429  * Shorthand for {@link Ext.ComponentMgr#registerPlugin}
14430  * @param {String} ptype The {@link Ext.component#ptype mnemonic string} by which the Plugin class
14431  * may be looked up.
14432  * @param {Constructor} cls The new Plugin class.
14433  * @member Ext
14434  * @method preg
14435  */
14436 Ext.preg = Ext.ComponentMgr.registerPlugin;
14437 /**
14438  * Shorthand for {@link Ext.ComponentMgr#create}
14439  * Creates a new Component from the specified config object using the
14440  * config object's {@link Ext.component#xtype xtype} to determine the class to instantiate.
14441  * @param {Object} config A configuration object for the Component you wish to create.
14442  * @param {Constructor} defaultType The constructor to provide the default Component type if
14443  * the config object does not contain a <code>xtype</code>. (Optional if the config contains a <code>xtype</code>).
14444  * @return {Ext.Component} The newly instantiated Component.
14445  * @member Ext
14446  * @method create
14447  */
14448 Ext.create = Ext.ComponentMgr.create;/**
14449  * @class Ext.Component
14450  * @extends Ext.util.Observable
14451  * <p>Base class for all Ext components.  All subclasses of Component may participate in the automated
14452  * Ext component lifecycle of creation, rendering and destruction which is provided by the {@link Ext.Container Container} class.
14453  * Components may be added to a Container through the {@link Ext.Container#items items} config option at the time the Container is created,
14454  * or they may be added dynamically via the {@link Ext.Container#add add} method.</p>
14455  * <p>The Component base class has built-in support for basic hide/show and enable/disable behavior.</p>
14456  * <p>All Components are registered with the {@link Ext.ComponentMgr} on construction so that they can be referenced at any time via
14457  * {@link Ext#getCmp}, passing the {@link #id}.</p>
14458  * <p>All user-developed visual widgets that are required to participate in automated lifecycle and size management should subclass Component (or
14459  * {@link Ext.BoxComponent} if managed box model handling is required, ie height and width management).</p>
14460  * <p>See the <a href="http://extjs.com/learn/Tutorial:Creating_new_UI_controls">Creating new UI controls</a> tutorial for details on how
14461  * and to either extend or augment ExtJs base classes to create custom Components.</p>
14462  * <p>Every component has a specific xtype, which is its Ext-specific type name, along with methods for checking the
14463  * xtype like {@link #getXType} and {@link #isXType}. This is the list of all valid xtypes:</p>
14464  * <pre>
14465 xtype            Class
14466 -------------    ------------------
14467 box              {@link Ext.BoxComponent}
14468 button           {@link Ext.Button}
14469 buttongroup      {@link Ext.ButtonGroup}
14470 colorpalette     {@link Ext.ColorPalette}
14471 component        {@link Ext.Component}
14472 container        {@link Ext.Container}
14473 cycle            {@link Ext.CycleButton}
14474 dataview         {@link Ext.DataView}
14475 datepicker       {@link Ext.DatePicker}
14476 editor           {@link Ext.Editor}
14477 editorgrid       {@link Ext.grid.EditorGridPanel}
14478 flash            {@link Ext.FlashComponent}
14479 grid             {@link Ext.grid.GridPanel}
14480 listview         {@link Ext.ListView}
14481 panel            {@link Ext.Panel}
14482 progress         {@link Ext.ProgressBar}
14483 propertygrid     {@link Ext.grid.PropertyGrid}
14484 slider           {@link Ext.Slider}
14485 spacer           {@link Ext.Spacer}
14486 splitbutton      {@link Ext.SplitButton}
14487 tabpanel         {@link Ext.TabPanel}
14488 treepanel        {@link Ext.tree.TreePanel}
14489 viewport         {@link Ext.ViewPort}
14490 window           {@link Ext.Window}
14491
14492 Toolbar components
14493 ---------------------------------------
14494 paging           {@link Ext.PagingToolbar}
14495 toolbar          {@link Ext.Toolbar}
14496 tbbutton         {@link Ext.Toolbar.Button}        (deprecated; use button)
14497 tbfill           {@link Ext.Toolbar.Fill}
14498 tbitem           {@link Ext.Toolbar.Item}
14499 tbseparator      {@link Ext.Toolbar.Separator}
14500 tbspacer         {@link Ext.Toolbar.Spacer}
14501 tbsplit          {@link Ext.Toolbar.SplitButton}   (deprecated; use splitbutton)
14502 tbtext           {@link Ext.Toolbar.TextItem}
14503
14504 Menu components
14505 ---------------------------------------
14506 menu             {@link Ext.menu.Menu}
14507 colormenu        {@link Ext.menu.ColorMenu}
14508 datemenu         {@link Ext.menu.DateMenu}
14509 menubaseitem     {@link Ext.menu.BaseItem}
14510 menucheckitem    {@link Ext.menu.CheckItem}
14511 menuitem         {@link Ext.menu.Item}
14512 menuseparator    {@link Ext.menu.Separator}
14513 menutextitem     {@link Ext.menu.TextItem}
14514
14515 Form components
14516 ---------------------------------------
14517 form             {@link Ext.form.FormPanel}
14518 checkbox         {@link Ext.form.Checkbox}
14519 checkboxgroup    {@link Ext.form.CheckboxGroup}
14520 combo            {@link Ext.form.ComboBox}
14521 datefield        {@link Ext.form.DateField}
14522 displayfield     {@link Ext.form.DisplayField}
14523 field            {@link Ext.form.Field}
14524 fieldset         {@link Ext.form.FieldSet}
14525 hidden           {@link Ext.form.Hidden}
14526 htmleditor       {@link Ext.form.HtmlEditor}
14527 label            {@link Ext.form.Label}
14528 numberfield      {@link Ext.form.NumberField}
14529 radio            {@link Ext.form.Radio}
14530 radiogroup       {@link Ext.form.RadioGroup}
14531 textarea         {@link Ext.form.TextArea}
14532 textfield        {@link Ext.form.TextField}
14533 timefield        {@link Ext.form.TimeField}
14534 trigger          {@link Ext.form.TriggerField}
14535
14536 Chart components
14537 ---------------------------------------
14538 chart            {@link Ext.chart.Chart}
14539 barchart         {@link Ext.chart.BarChart}
14540 cartesianchart   {@link Ext.chart.CartesianChart}
14541 columnchart      {@link Ext.chart.ColumnChart}
14542 linechart        {@link Ext.chart.LineChart}
14543 piechart         {@link Ext.chart.PieChart}
14544
14545 Store xtypes
14546 ---------------------------------------
14547 arraystore       {@link Ext.data.ArrayStore}
14548 directstore      {@link Ext.data.DirectStore}
14549 groupingstore    {@link Ext.data.GroupingStore}
14550 jsonstore        {@link Ext.data.JsonStore}
14551 simplestore      {@link Ext.data.SimpleStore}      (deprecated; use arraystore)
14552 store            {@link Ext.data.Store}
14553 xmlstore         {@link Ext.data.XmlStore}
14554 </pre>
14555  * @constructor
14556  * @param {Ext.Element/String/Object} config The configuration options may be specified as either:
14557  * <div class="mdetail-params"><ul>
14558  * <li><b>an element</b> :
14559  * <p class="sub-desc">it is set as the internal element and its id used as the component id</p></li>
14560  * <li><b>a string</b> :
14561  * <p class="sub-desc">it is assumed to be the id of an existing element and is used as the component id</p></li>
14562  * <li><b>anything else</b> :
14563  * <p class="sub-desc">it is assumed to be a standard config object and is applied to the component</p></li>
14564  * </ul></div>
14565  */
14566 Ext.Component = function(config){
14567     config = config || {};
14568     if(config.initialConfig){
14569         if(config.isAction){           // actions
14570             this.baseAction = config;
14571         }
14572         config = config.initialConfig; // component cloning / action set up
14573     }else if(config.tagName || config.dom || Ext.isString(config)){ // element object
14574         config = {applyTo: config, id: config.id || config};
14575     }
14576
14577     /**
14578      * This Component's initial configuration specification. Read-only.
14579      * @type Object
14580      * @property initialConfig
14581      */
14582     this.initialConfig = config;
14583
14584     Ext.apply(this, config);
14585     this.addEvents(
14586         /**
14587          * @event added
14588          * Fires when a component is added to an Ext.Container
14589          * @param {Ext.Component} this
14590          * @param {Ext.Container} ownerCt Container which holds the component
14591          * @param {number} index Position at which the component was added
14592          */
14593         'added',
14594         /**
14595          * @event disable
14596          * Fires after the component is disabled.
14597          * @param {Ext.Component} this
14598          */
14599         'disable',
14600         /**
14601          * @event enable
14602          * Fires after the component is enabled.
14603          * @param {Ext.Component} this
14604          */
14605         'enable',
14606         /**
14607          * @event beforeshow
14608          * Fires before the component is shown by calling the {@link #show} method.
14609          * Return false from an event handler to stop the show.
14610          * @param {Ext.Component} this
14611          */
14612         'beforeshow',
14613         /**
14614          * @event show
14615          * Fires after the component is shown when calling the {@link #show} method.
14616          * @param {Ext.Component} this
14617          */
14618         'show',
14619         /**
14620          * @event beforehide
14621          * Fires before the component is hidden by calling the {@link #hide} method.
14622          * Return false from an event handler to stop the hide.
14623          * @param {Ext.Component} this
14624          */
14625         'beforehide',
14626         /**
14627          * @event hide
14628          * Fires after the component is hidden.
14629          * Fires after the component is hidden when calling the {@link #hide} method.
14630          * @param {Ext.Component} this
14631          */
14632         'hide',
14633         /**
14634          * @event removed
14635          * Fires when a component is removed from an Ext.Container
14636          * @param {Ext.Component} this
14637          * @param {Ext.Container} ownerCt Container which holds the component
14638          */
14639         'removed',
14640         /**
14641          * @event beforerender
14642          * Fires before the component is {@link #rendered}. Return false from an
14643          * event handler to stop the {@link #render}.
14644          * @param {Ext.Component} this
14645          */
14646         'beforerender',
14647         /**
14648          * @event render
14649          * Fires after the component markup is {@link #rendered}.
14650          * @param {Ext.Component} this
14651          */
14652         'render',
14653         /**
14654          * @event afterrender
14655          * <p>Fires after the component rendering is finished.</p>
14656          * <p>The afterrender event is fired after this Component has been {@link #rendered}, been postprocesed
14657          * by any afterRender method defined for the Component, and, if {@link #stateful}, after state
14658          * has been restored.</p>
14659          * @param {Ext.Component} this
14660          */
14661         'afterrender',
14662         /**
14663          * @event beforedestroy
14664          * Fires before the component is {@link #destroy}ed. Return false from an event handler to stop the {@link #destroy}.
14665          * @param {Ext.Component} this
14666          */
14667         'beforedestroy',
14668         /**
14669          * @event destroy
14670          * Fires after the component is {@link #destroy}ed.
14671          * @param {Ext.Component} this
14672          */
14673         'destroy',
14674         /**
14675          * @event beforestaterestore
14676          * Fires before the state of the component is restored. Return false from an event handler to stop the restore.
14677          * @param {Ext.Component} this
14678          * @param {Object} state The hash of state values returned from the StateProvider. If this
14679          * event is not vetoed, then the state object is passed to <b><tt>applyState</tt></b>. By default,
14680          * that simply copies property values into this Component. The method maybe overriden to
14681          * provide custom state restoration.
14682          */
14683         'beforestaterestore',
14684         /**
14685          * @event staterestore
14686          * Fires after the state of the component is restored.
14687          * @param {Ext.Component} this
14688          * @param {Object} state The hash of state values returned from the StateProvider. This is passed
14689          * to <b><tt>applyState</tt></b>. By default, that simply copies property values into this
14690          * Component. The method maybe overriden to provide custom state restoration.
14691          */
14692         'staterestore',
14693         /**
14694          * @event beforestatesave
14695          * Fires before the state of the component is saved to the configured state provider. Return false to stop the save.
14696          * @param {Ext.Component} this
14697          * @param {Object} state The hash of state values. This is determined by calling
14698          * <b><tt>getState()</tt></b> on the Component. This method must be provided by the
14699          * developer to return whetever representation of state is required, by default, Ext.Component
14700          * has a null implementation.
14701          */
14702         'beforestatesave',
14703         /**
14704          * @event statesave
14705          * Fires after the state of the component is saved to the configured state provider.
14706          * @param {Ext.Component} this
14707          * @param {Object} state The hash of state values. This is determined by calling
14708          * <b><tt>getState()</tt></b> on the Component. This method must be provided by the
14709          * developer to return whetever representation of state is required, by default, Ext.Component
14710          * has a null implementation.
14711          */
14712         'statesave'
14713     );
14714     this.getId();
14715     Ext.ComponentMgr.register(this);
14716     Ext.Component.superclass.constructor.call(this);
14717
14718     if(this.baseAction){
14719         this.baseAction.addComponent(this);
14720     }
14721
14722     this.initComponent();
14723
14724     if(this.plugins){
14725         if(Ext.isArray(this.plugins)){
14726             for(var i = 0, len = this.plugins.length; i < len; i++){
14727                 this.plugins[i] = this.initPlugin(this.plugins[i]);
14728             }
14729         }else{
14730             this.plugins = this.initPlugin(this.plugins);
14731         }
14732     }
14733
14734     if(this.stateful !== false){
14735         this.initState();
14736     }
14737
14738     if(this.applyTo){
14739         this.applyToMarkup(this.applyTo);
14740         delete this.applyTo;
14741     }else if(this.renderTo){
14742         this.render(this.renderTo);
14743         delete this.renderTo;
14744     }
14745 };
14746
14747 // private
14748 Ext.Component.AUTO_ID = 1000;
14749
14750 Ext.extend(Ext.Component, Ext.util.Observable, {
14751     // Configs below are used for all Components when rendered by FormLayout.
14752     /**
14753      * @cfg {String} fieldLabel <p>The label text to display next to this Component (defaults to '').</p>
14754      * <br><p><b>Note</b>: this config is only used when this Component is rendered by a Container which
14755      * has been configured to use the <b>{@link Ext.layout.FormLayout FormLayout}</b> layout manager (e.g.
14756      * {@link Ext.form.FormPanel} or specifying <tt>layout:'form'</tt>).</p><br>
14757      * <p>Also see <tt>{@link #hideLabel}</tt> and
14758      * {@link Ext.layout.FormLayout}.{@link Ext.layout.FormLayout#fieldTpl fieldTpl}.</p>
14759      * Example use:<pre><code>
14760 new Ext.FormPanel({
14761     height: 100,
14762     renderTo: Ext.getBody(),
14763     items: [{
14764         xtype: 'textfield',
14765         fieldLabel: 'Name'
14766     }]
14767 });
14768 </code></pre>
14769      */
14770     /**
14771      * @cfg {String} labelStyle <p>A CSS style specification string to apply directly to this field's
14772      * label.  Defaults to the container's labelStyle value if set (e.g.,
14773      * <tt>{@link Ext.layout.FormLayout#labelStyle}</tt> , or '').</p>
14774      * <br><p><b>Note</b>: see the note for <code>{@link #clearCls}</code>.</p><br>
14775      * <p>Also see <code>{@link #hideLabel}</code> and
14776      * <code>{@link Ext.layout.FormLayout}.{@link Ext.layout.FormLayout#fieldTpl fieldTpl}.</code></p>
14777      * Example use:<pre><code>
14778 new Ext.FormPanel({
14779     height: 100,
14780     renderTo: Ext.getBody(),
14781     items: [{
14782         xtype: 'textfield',
14783         fieldLabel: 'Name',
14784         labelStyle: 'font-weight:bold;'
14785     }]
14786 });
14787 </code></pre>
14788      */
14789     /**
14790      * @cfg {String} labelSeparator <p>The separator to display after the text of each
14791      * <tt>{@link #fieldLabel}</tt>.  This property may be configured at various levels.
14792      * The order of precedence is:
14793      * <div class="mdetail-params"><ul>
14794      * <li>field / component level</li>
14795      * <li>container level</li>
14796      * <li>{@link Ext.layout.FormLayout#labelSeparator layout level} (defaults to colon <tt>':'</tt>)</li>
14797      * </ul></div>
14798      * To display no separator for this field's label specify empty string ''.</p>
14799      * <br><p><b>Note</b>: see the note for <tt>{@link #clearCls}</tt>.</p><br>
14800      * <p>Also see <tt>{@link #hideLabel}</tt> and
14801      * {@link Ext.layout.FormLayout}.{@link Ext.layout.FormLayout#fieldTpl fieldTpl}.</p>
14802      * Example use:<pre><code>
14803 new Ext.FormPanel({
14804     height: 100,
14805     renderTo: Ext.getBody(),
14806     layoutConfig: {
14807         labelSeparator: '~'   // layout config has lowest priority (defaults to ':')
14808     },
14809     {@link Ext.layout.FormLayout#labelSeparator labelSeparator}: '>>',     // config at container level
14810     items: [{
14811         xtype: 'textfield',
14812         fieldLabel: 'Field 1',
14813         labelSeparator: '...' // field/component level config supersedes others
14814     },{
14815         xtype: 'textfield',
14816         fieldLabel: 'Field 2' // labelSeparator will be '='
14817     }]
14818 });
14819 </code></pre>
14820      */
14821     /**
14822      * @cfg {Boolean} hideLabel <p><tt>true</tt> to completely hide the label element
14823      * ({@link #fieldLabel label} and {@link #labelSeparator separator}). Defaults to <tt>false</tt>.
14824      * By default, even if you do not specify a <tt>{@link #fieldLabel}</tt> the space will still be
14825      * reserved so that the field will line up with other fields that do have labels.
14826      * Setting this to <tt>true</tt> will cause the field to not reserve that space.</p>
14827      * <br><p><b>Note</b>: see the note for <tt>{@link #clearCls}</tt>.</p><br>
14828      * Example use:<pre><code>
14829 new Ext.FormPanel({
14830     height: 100,
14831     renderTo: Ext.getBody(),
14832     items: [{
14833         xtype: 'textfield'
14834         hideLabel: true
14835     }]
14836 });
14837 </code></pre>
14838      */
14839     /**
14840      * @cfg {String} clearCls <p>The CSS class used to to apply to the special clearing div rendered
14841      * directly after each form field wrapper to provide field clearing (defaults to
14842      * <tt>'x-form-clear-left'</tt>).</p>
14843      * <br><p><b>Note</b>: this config is only used when this Component is rendered by a Container
14844      * which has been configured to use the <b>{@link Ext.layout.FormLayout FormLayout}</b> layout
14845      * manager (e.g. {@link Ext.form.FormPanel} or specifying <tt>layout:'form'</tt>) and either a
14846      * <tt>{@link #fieldLabel}</tt> is specified or <tt>isFormField=true</tt> is specified.</p><br>
14847      * <p>See {@link Ext.layout.FormLayout}.{@link Ext.layout.FormLayout#fieldTpl fieldTpl} also.</p>
14848      */
14849     /**
14850      * @cfg {String} itemCls
14851      * <p><b>Note</b>: this config is only used when this Component is rendered by a Container which
14852      * has been configured to use the <b>{@link Ext.layout.FormLayout FormLayout}</b> layout manager (e.g.
14853      * {@link Ext.form.FormPanel} or specifying <tt>layout:'form'</tt>).</p><br>
14854      * <p>An additional CSS class to apply to the div wrapping the form item
14855      * element of this field.  If supplied, <tt>itemCls</tt> at the <b>field</b> level will override
14856      * the default <tt>itemCls</tt> supplied at the <b>container</b> level. The value specified for
14857      * <tt>itemCls</tt> will be added to the default class (<tt>'x-form-item'</tt>).</p>
14858      * <p>Since it is applied to the item wrapper (see
14859      * {@link Ext.layout.FormLayout}.{@link Ext.layout.FormLayout#fieldTpl fieldTpl}), it allows
14860      * you to write standard CSS rules that can apply to the field, the label (if specified), or
14861      * any other element within the markup for the field.</p>
14862      * <br><p><b>Note</b>: see the note for <tt>{@link #fieldLabel}</tt>.</p><br>
14863      * Example use:<pre><code>
14864 // Apply a style to the field&#39;s label:
14865 &lt;style>
14866     .required .x-form-item-label {font-weight:bold;color:red;}
14867 &lt;/style>
14868
14869 new Ext.FormPanel({
14870     height: 100,
14871     renderTo: Ext.getBody(),
14872     items: [{
14873         xtype: 'textfield',
14874         fieldLabel: 'Name',
14875         itemCls: 'required' //this label will be styled
14876     },{
14877         xtype: 'textfield',
14878         fieldLabel: 'Favorite Color'
14879     }]
14880 });
14881 </code></pre>
14882      */
14883
14884     /**
14885      * @cfg {String} id
14886      * <p>The <b>unique</b> id of this component (defaults to an {@link #getId auto-assigned id}).
14887      * You should assign an id if you need to be able to access the component later and you do
14888      * not have an object reference available (e.g., using {@link Ext}.{@link Ext#getCmp getCmp}).</p>
14889      * <p>Note that this id will also be used as the element id for the containing HTML element
14890      * that is rendered to the page for this component. This allows you to write id-based CSS
14891      * rules to style the specific instance of this component uniquely, and also to select
14892      * sub-elements using this component's id as the parent.</p>
14893      * <p><b>Note</b>: to avoid complications imposed by a unique <tt>id</tt> also see
14894      * <code>{@link #itemId}</code> and <code>{@link #ref}</code>.</p>
14895      * <p><b>Note</b>: to access the container of an item see <code>{@link #ownerCt}</code>.</p>
14896      */
14897     /**
14898      * @cfg {String} itemId
14899      * <p>An <tt>itemId</tt> can be used as an alternative way to get a reference to a component
14900      * when no object reference is available.  Instead of using an <code>{@link #id}</code> with
14901      * {@link Ext}.{@link Ext#getCmp getCmp}, use <code>itemId</code> with
14902      * {@link Ext.Container}.{@link Ext.Container#getComponent getComponent} which will retrieve
14903      * <code>itemId</code>'s or <tt>{@link #id}</tt>'s. Since <code>itemId</code>'s are an index to the
14904      * container's internal MixedCollection, the <code>itemId</code> is scoped locally to the container --
14905      * avoiding potential conflicts with {@link Ext.ComponentMgr} which requires a <b>unique</b>
14906      * <code>{@link #id}</code>.</p>
14907      * <pre><code>
14908 var c = new Ext.Panel({ //
14909     {@link Ext.BoxComponent#height height}: 300,
14910     {@link #renderTo}: document.body,
14911     {@link Ext.Container#layout layout}: 'auto',
14912     {@link Ext.Container#items items}: [
14913         {
14914             itemId: 'p1',
14915             {@link Ext.Panel#title title}: 'Panel 1',
14916             {@link Ext.BoxComponent#height height}: 150
14917         },
14918         {
14919             itemId: 'p2',
14920             {@link Ext.Panel#title title}: 'Panel 2',
14921             {@link Ext.BoxComponent#height height}: 150
14922         }
14923     ]
14924 })
14925 p1 = c.{@link Ext.Container#getComponent getComponent}('p1'); // not the same as {@link Ext#getCmp Ext.getCmp()}
14926 p2 = p1.{@link #ownerCt}.{@link Ext.Container#getComponent getComponent}('p2'); // reference via a sibling
14927      * </code></pre>
14928      * <p>Also see <tt>{@link #id}</tt> and <code>{@link #ref}</code>.</p>
14929      * <p><b>Note</b>: to access the container of an item see <tt>{@link #ownerCt}</tt>.</p>
14930      */
14931     /**
14932      * @cfg {String} xtype
14933      * The registered <tt>xtype</tt> to create. This config option is not used when passing
14934      * a config object into a constructor. This config option is used only when
14935      * lazy instantiation is being used, and a child item of a Container is being
14936      * specified not as a fully instantiated Component, but as a <i>Component config
14937      * object</i>. The <tt>xtype</tt> will be looked up at render time up to determine what
14938      * type of child Component to create.<br><br>
14939      * The predefined xtypes are listed {@link Ext.Component here}.
14940      * <br><br>
14941      * If you subclass Components to create your own Components, you may register
14942      * them using {@link Ext.ComponentMgr#registerType} in order to be able to
14943      * take advantage of lazy instantiation and rendering.
14944      */
14945     /**
14946      * @cfg {String} ptype
14947      * The registered <tt>ptype</tt> to create. This config option is not used when passing
14948      * a config object into a constructor. This config option is used only when
14949      * lazy instantiation is being used, and a Plugin is being
14950      * specified not as a fully instantiated Component, but as a <i>Component config
14951      * object</i>. The <tt>ptype</tt> will be looked up at render time up to determine what
14952      * type of Plugin to create.<br><br>
14953      * If you create your own Plugins, you may register them using
14954      * {@link Ext.ComponentMgr#registerPlugin} in order to be able to
14955      * take advantage of lazy instantiation and rendering.
14956      */
14957     /**
14958      * @cfg {String} cls
14959      * An optional extra CSS class that will be added to this component's Element (defaults to '').  This can be
14960      * useful for adding customized styles to the component or any of its children using standard CSS rules.
14961      */
14962     /**
14963      * @cfg {String} overCls
14964      * An optional extra CSS class that will be added to this component's Element when the mouse moves
14965      * over the Element, and removed when the mouse moves out. (defaults to '').  This can be
14966      * useful for adding customized 'active' or 'hover' styles to the component or any of its children using standard CSS rules.
14967      */
14968     /**
14969      * @cfg {String} style
14970      * A custom style specification to be applied to this component's Element.  Should be a valid argument to
14971      * {@link Ext.Element#applyStyles}.
14972      * <pre><code>
14973 new Ext.Panel({
14974     title: 'Some Title',
14975     renderTo: Ext.getBody(),
14976     width: 400, height: 300,
14977     layout: 'form',
14978     items: [{
14979         xtype: 'textarea',
14980         style: {
14981             width: '95%',
14982             marginBottom: '10px'
14983         }
14984     },
14985         new Ext.Button({
14986             text: 'Send',
14987             minWidth: '100',
14988             style: {
14989                 marginBottom: '10px'
14990             }
14991         })
14992     ]
14993 });
14994      * </code></pre>
14995      */
14996     /**
14997      * @cfg {String} ctCls
14998      * <p>An optional extra CSS class that will be added to this component's container. This can be useful for
14999      * adding customized styles to the container or any of its children using standard CSS rules.  See
15000      * {@link Ext.layout.ContainerLayout}.{@link Ext.layout.ContainerLayout#extraCls extraCls} also.</p>
15001      * <p><b>Note</b>: <tt>ctCls</tt> defaults to <tt>''</tt> except for the following class
15002      * which assigns a value by default:
15003      * <div class="mdetail-params"><ul>
15004      * <li>{@link Ext.layout.Box Box Layout} : <tt>'x-box-layout-ct'</tt></li>
15005      * </ul></div>
15006      * To configure the above Class with an extra CSS class append to the default.  For example,
15007      * for BoxLayout (Hbox and Vbox):<pre><code>
15008      * ctCls: 'x-box-layout-ct custom-class'
15009      * </code></pre>
15010      * </p>
15011      */
15012     /**
15013      * @cfg {Boolean} disabled
15014      * Render this component disabled (default is false).
15015      */
15016     disabled : false,
15017     /**
15018      * @cfg {Boolean} hidden
15019      * Render this component hidden (default is false). If <tt>true</tt>, the
15020      * {@link #hide} method will be called internally.
15021      */
15022     hidden : false,
15023     /**
15024      * @cfg {Object/Array} plugins
15025      * An object or array of objects that will provide custom functionality for this component.  The only
15026      * requirement for a valid plugin is that it contain an init method that accepts a reference of type Ext.Component.
15027      * When a component is created, if any plugins are available, the component will call the init method on each
15028      * plugin, passing a reference to itself.  Each plugin can then call methods or respond to events on the
15029      * component as needed to provide its functionality.
15030      */
15031     /**
15032      * @cfg {Mixed} applyTo
15033      * <p>Specify the id of the element, a DOM element or an existing Element corresponding to a DIV
15034      * that is already present in the document that specifies some structural markup for this
15035      * component.</p><div><ul>
15036      * <li><b>Description</b> : <ul>
15037      * <div class="sub-desc">When <tt>applyTo</tt> is used, constituent parts of the component can also be specified
15038      * by id or CSS class name within the main element, and the component being created may attempt
15039      * to create its subcomponents from that markup if applicable.</div>
15040      * </ul></li>
15041      * <li><b>Notes</b> : <ul>
15042      * <div class="sub-desc">When using this config, a call to render() is not required.</div>
15043      * <div class="sub-desc">If applyTo is specified, any value passed for {@link #renderTo} will be ignored and the target
15044      * element's parent node will automatically be used as the component's container.</div>
15045      * </ul></li>
15046      * </ul></div>
15047      */
15048     /**
15049      * @cfg {Mixed} renderTo
15050      * <p>Specify the id of the element, a DOM element or an existing Element that this component
15051      * will be rendered into.</p><div><ul>
15052      * <li><b>Notes</b> : <ul>
15053      * <div class="sub-desc">Do <u>not</u> use this option if the Component is to be a child item of
15054      * a {@link Ext.Container Container}. It is the responsibility of the
15055      * {@link Ext.Container Container}'s {@link Ext.Container#layout layout manager}
15056      * to render and manage its child items.</div>
15057      * <div class="sub-desc">When using this config, a call to render() is not required.</div>
15058      * </ul></li>
15059      * </ul></div>
15060      * <p>See <tt>{@link #render}</tt> also.</p>
15061      */
15062     /**
15063      * @cfg {Boolean} stateful
15064      * <p>A flag which causes the Component to attempt to restore the state of
15065      * internal properties from a saved state on startup. The component must have
15066      * either a <code>{@link #stateId}</code> or <code>{@link #id}</code> assigned
15067      * for state to be managed. Auto-generated ids are not guaranteed to be stable
15068      * across page loads and cannot be relied upon to save and restore the same
15069      * state for a component.<p>
15070      * <p>For state saving to work, the state manager's provider must have been
15071      * set to an implementation of {@link Ext.state.Provider} which overrides the
15072      * {@link Ext.state.Provider#set set} and {@link Ext.state.Provider#get get}
15073      * methods to save and recall name/value pairs. A built-in implementation,
15074      * {@link Ext.state.CookieProvider} is available.</p>
15075      * <p>To set the state provider for the current page:</p>
15076      * <pre><code>
15077 Ext.state.Manager.setProvider(new Ext.state.CookieProvider({
15078     expires: new Date(new Date().getTime()+(1000*60*60*24*7)), //7 days from now
15079 }));
15080      * </code></pre>
15081      * <p>A stateful Component attempts to save state when one of the events
15082      * listed in the <code>{@link #stateEvents}</code> configuration fires.</p>
15083      * <p>To save state, a stateful Component first serializes its state by
15084      * calling <b><code>getState</code></b>. By default, this function does
15085      * nothing. The developer must provide an implementation which returns an
15086      * object hash which represents the Component's restorable state.</p>
15087      * <p>The value yielded by getState is passed to {@link Ext.state.Manager#set}
15088      * which uses the configured {@link Ext.state.Provider} to save the object
15089      * keyed by the Component's <code>{@link stateId}</code>, or, if that is not
15090      * specified, its <code>{@link #id}</code>.</p>
15091      * <p>During construction, a stateful Component attempts to <i>restore</i>
15092      * its state by calling {@link Ext.state.Manager#get} passing the
15093      * <code>{@link #stateId}</code>, or, if that is not specified, the
15094      * <code>{@link #id}</code>.</p>
15095      * <p>The resulting object is passed to <b><code>applyState</code></b>.
15096      * The default implementation of <code>applyState</code> simply copies
15097      * properties into the object, but a developer may override this to support
15098      * more behaviour.</p>
15099      * <p>You can perform extra processing on state save and restore by attaching
15100      * handlers to the {@link #beforestaterestore}, {@link #staterestore},
15101      * {@link #beforestatesave} and {@link #statesave} events.</p>
15102      */
15103     /**
15104      * @cfg {String} stateId
15105      * The unique id for this component to use for state management purposes
15106      * (defaults to the component id if one was set, otherwise null if the
15107      * component is using a generated id).
15108      * <p>See <code>{@link #stateful}</code> for an explanation of saving and
15109      * restoring Component state.</p>
15110      */
15111     /**
15112      * @cfg {Array} stateEvents
15113      * <p>An array of events that, when fired, should trigger this component to
15114      * save its state (defaults to none). <code>stateEvents</code> may be any type
15115      * of event supported by this component, including browser or custom events
15116      * (e.g., <tt>['click', 'customerchange']</tt>).</p>
15117      * <p>See <code>{@link #stateful}</code> for an explanation of saving and
15118      * restoring Component state.</p>
15119      */
15120     /**
15121      * @cfg {Mixed} autoEl
15122      * <p>A tag name or {@link Ext.DomHelper DomHelper} spec used to create the {@link #getEl Element} which will
15123      * encapsulate this Component.</p>
15124      * <p>You do not normally need to specify this. For the base classes {@link Ext.Component}, {@link Ext.BoxComponent},
15125      * and {@link Ext.Container}, this defaults to <b><tt>'div'</tt></b>. The more complex Ext classes use a more complex
15126      * DOM structure created by their own onRender methods.</p>
15127      * <p>This is intended to allow the developer to create application-specific utility Components encapsulated by
15128      * different DOM elements. Example usage:</p><pre><code>
15129 {
15130     xtype: 'box',
15131     autoEl: {
15132         tag: 'img',
15133         src: 'http://www.example.com/example.jpg'
15134     }
15135 }, {
15136     xtype: 'box',
15137     autoEl: {
15138         tag: 'blockquote',
15139         html: 'autoEl is cool!'
15140     }
15141 }, {
15142     xtype: 'container',
15143     autoEl: 'ul',
15144     cls: 'ux-unordered-list',
15145     items: {
15146         xtype: 'box',
15147         autoEl: 'li',
15148         html: 'First list item'
15149     }
15150 }
15151 </code></pre>
15152      */
15153     autoEl : 'div',
15154
15155     /**
15156      * @cfg {String} disabledClass
15157      * CSS class added to the component when it is disabled (defaults to 'x-item-disabled').
15158      */
15159     disabledClass : 'x-item-disabled',
15160     /**
15161      * @cfg {Boolean} allowDomMove
15162      * Whether the component can move the Dom node when rendering (defaults to true).
15163      */
15164     allowDomMove : true,
15165     /**
15166      * @cfg {Boolean} autoShow
15167      * True if the component should check for hidden classes (e.g. 'x-hidden' or 'x-hide-display') and remove
15168      * them on render (defaults to false).
15169      */
15170     autoShow : false,
15171     /**
15172      * @cfg {String} hideMode
15173      * <p>How this component should be hidden. Supported values are <tt>'visibility'</tt>
15174      * (css visibility), <tt>'offsets'</tt> (negative offset position) and <tt>'display'</tt>
15175      * (css display).</p>
15176      * <br><p><b>Note</b>: the default of <tt>'display'</tt> is generally preferred
15177      * since items are automatically laid out when they are first shown (no sizing
15178      * is done while hidden).</p>
15179      */
15180     hideMode : 'display',
15181     /**
15182      * @cfg {Boolean} hideParent
15183      * True to hide and show the component's container when hide/show is called on the component, false to hide
15184      * and show the component itself (defaults to false).  For example, this can be used as a shortcut for a hide
15185      * button on a window by setting hide:true on the button when adding it to its parent container.
15186      */
15187     hideParent : false,
15188     /**
15189      * <p>The {@link Ext.Element} which encapsulates this Component. Read-only.</p>
15190      * <p>This will <i>usually</i> be a &lt;DIV> element created by the class's onRender method, but
15191      * that may be overridden using the <code>{@link #autoEl}</code> config.</p>
15192      * <br><p><b>Note</b>: this element will not be available until this Component has been rendered.</p><br>
15193      * <p>To add listeners for <b>DOM events</b> to this Component (as opposed to listeners
15194      * for this Component's own Observable events), see the {@link Ext.util.Observable#listeners listeners}
15195      * config for a suggestion, or use a render listener directly:</p><pre><code>
15196 new Ext.Panel({
15197     title: 'The Clickable Panel',
15198     listeners: {
15199         render: function(p) {
15200             // Append the Panel to the click handler&#39;s argument list.
15201             p.getEl().on('click', handlePanelClick.createDelegate(null, [p], true));
15202         },
15203         single: true  // Remove the listener after first invocation
15204     }
15205 });
15206 </code></pre>
15207      * <p>See also <tt>{@link #getEl getEl}</p>
15208      * @type Ext.Element
15209      * @property el
15210      */
15211     /**
15212      * This Component's owner {@link Ext.Container Container} (defaults to undefined, and is set automatically when
15213      * this Component is added to a Container).  Read-only.
15214      * <p><b>Note</b>: to access items within the Container see <tt>{@link #itemId}</tt>.</p>
15215      * @type Ext.Container
15216      * @property ownerCt
15217      */
15218     /**
15219      * True if this component is hidden. Read-only.
15220      * @type Boolean
15221      * @property hidden
15222      */
15223     /**
15224      * True if this component is disabled. Read-only.
15225      * @type Boolean
15226      * @property disabled
15227      */
15228     /**
15229      * True if this component has been rendered. Read-only.
15230      * @type Boolean
15231      * @property rendered
15232      */
15233     rendered : false,
15234
15235     /**
15236      * @cfg {String} contentEl
15237      * <p>Optional. Specify an existing HTML element, or the <code>id</code> of an existing HTML element to use as the content
15238      * for this component.</p>
15239      * <ul>
15240      * <li><b>Description</b> :
15241      * <div class="sub-desc">This config option is used to take an existing HTML element and place it in the layout element
15242      * of a new component (it simply moves the specified DOM element <i>after the Component is rendered</i> to use as the content.</div></li>
15243      * <li><b>Notes</b> :
15244      * <div class="sub-desc">The specified HTML element is appended to the layout element of the component <i>after any configured
15245      * {@link #html HTML} has been inserted</i>, and so the document will not contain this element at the time the {@link #render} event is fired.</div>
15246      * <div class="sub-desc">The specified HTML element used will not participate in any <code><b>{@link Ext.Container#layout layout}</b></code>
15247      * scheme that the Component may use. It is just HTML. Layouts operate on child <code><b>{@link Ext.Container#items items}</b></code>.</div>
15248      * <div class="sub-desc">Add either the <code>x-hidden</code> or the <code>x-hide-display</code> CSS class to
15249      * prevent a brief flicker of the content before it is rendered to the panel.</div></li>
15250      * </ul>
15251      */
15252     /**
15253      * @cfg {String/Object} html
15254      * An HTML fragment, or a {@link Ext.DomHelper DomHelper} specification to use as the layout element
15255      * content (defaults to ''). The HTML content is added after the component is rendered,
15256      * so the document will not contain this HTML at the time the {@link #render} event is fired.
15257      * This content is inserted into the body <i>before</i> any configured {@link #contentEl} is appended.
15258      */
15259
15260     /**
15261      * @cfg {Mixed} tpl
15262      * An <bold>{@link Ext.Template}</bold>, <bold>{@link Ext.XTemplate}</bold>
15263      * or an array of strings to form an Ext.XTemplate.
15264      * Used in conjunction with the <code>{@link #data}</code> and
15265      * <code>{@link #tplWriteMode}</code> configurations.
15266      */
15267
15268     /**
15269      * @cfg {String} tplWriteMode The Ext.(X)Template method to use when
15270      * updating the content area of the Component. Defaults to <tt>'overwrite'</tt>
15271      * (see <code>{@link Ext.XTemplate#overwrite}</code>).
15272      */
15273     tplWriteMode : 'overwrite',
15274
15275     /**
15276      * @cfg {Mixed} data
15277      * The initial set of data to apply to the <code>{@link #tpl}</code> to
15278      * update the content area of the Component.
15279      */
15280
15281
15282     // private
15283     ctype : 'Ext.Component',
15284
15285     // private
15286     actionMode : 'el',
15287
15288     // private
15289     getActionEl : function(){
15290         return this[this.actionMode];
15291     },
15292
15293     initPlugin : function(p){
15294         if(p.ptype && !Ext.isFunction(p.init)){
15295             p = Ext.ComponentMgr.createPlugin(p);
15296         }else if(Ext.isString(p)){
15297             p = Ext.ComponentMgr.createPlugin({
15298                 ptype: p
15299             });
15300         }
15301         p.init(this);
15302         return p;
15303     },
15304
15305     /* // protected
15306      * Function to be implemented by Component subclasses to be part of standard component initialization flow (it is empty by default).
15307      * <pre><code>
15308 // Traditional constructor:
15309 Ext.Foo = function(config){
15310     // call superclass constructor:
15311     Ext.Foo.superclass.constructor.call(this, config);
15312
15313     this.addEvents({
15314         // add events
15315     });
15316 };
15317 Ext.extend(Ext.Foo, Ext.Bar, {
15318    // class body
15319 }
15320
15321 // initComponent replaces the constructor:
15322 Ext.Foo = Ext.extend(Ext.Bar, {
15323     initComponent : function(){
15324         // call superclass initComponent
15325         Ext.Container.superclass.initComponent.call(this);
15326
15327         this.addEvents({
15328             // add events
15329         });
15330     }
15331 }
15332 </code></pre>
15333      */
15334     initComponent : Ext.emptyFn,
15335
15336     /**
15337      * <p>Render this Component into the passed HTML element.</p>
15338      * <p><b>If you are using a {@link Ext.Container Container} object to house this Component, then
15339      * do not use the render method.</b></p>
15340      * <p>A Container's child Components are rendered by that Container's
15341      * {@link Ext.Container#layout layout} manager when the Container is first rendered.</p>
15342      * <p>Certain layout managers allow dynamic addition of child components. Those that do
15343      * include {@link Ext.layout.CardLayout}, {@link Ext.layout.AnchorLayout},
15344      * {@link Ext.layout.FormLayout}, {@link Ext.layout.TableLayout}.</p>
15345      * <p>If the Container is already rendered when a new child Component is added, you may need to call
15346      * the Container's {@link Ext.Container#doLayout doLayout} to refresh the view which causes any
15347      * unrendered child Components to be rendered. This is required so that you can add multiple
15348      * child components if needed while only refreshing the layout once.</p>
15349      * <p>When creating complex UIs, it is important to remember that sizing and positioning
15350      * of child items is the responsibility of the Container's {@link Ext.Container#layout layout} manager.
15351      * If you expect child items to be sized in response to user interactions, you must
15352      * configure the Container with a layout manager which creates and manages the type of layout you
15353      * have in mind.</p>
15354      * <p><b>Omitting the Container's {@link Ext.Container#layout layout} config means that a basic
15355      * layout manager is used which does nothing but render child components sequentially into the
15356      * Container. No sizing or positioning will be performed in this situation.</b></p>
15357      * @param {Element/HTMLElement/String} container (optional) The element this Component should be
15358      * rendered into. If it is being created from existing markup, this should be omitted.
15359      * @param {String/Number} position (optional) The element ID or DOM node index within the container <b>before</b>
15360      * which this component will be inserted (defaults to appending to the end of the container)
15361      */
15362     render : function(container, position){
15363         if(!this.rendered && this.fireEvent('beforerender', this) !== false){
15364             if(!container && this.el){
15365                 this.el = Ext.get(this.el);
15366                 container = this.el.dom.parentNode;
15367                 this.allowDomMove = false;
15368             }
15369             this.container = Ext.get(container);
15370             if(this.ctCls){
15371                 this.container.addClass(this.ctCls);
15372             }
15373             this.rendered = true;
15374             if(position !== undefined){
15375                 if(Ext.isNumber(position)){
15376                     position = this.container.dom.childNodes[position];
15377                 }else{
15378                     position = Ext.getDom(position);
15379                 }
15380             }
15381             this.onRender(this.container, position || null);
15382             if(this.autoShow){
15383                 this.el.removeClass(['x-hidden','x-hide-' + this.hideMode]);
15384             }
15385             if(this.cls){
15386                 this.el.addClass(this.cls);
15387                 delete this.cls;
15388             }
15389             if(this.style){
15390                 this.el.applyStyles(this.style);
15391                 delete this.style;
15392             }
15393             if(this.overCls){
15394                 this.el.addClassOnOver(this.overCls);
15395             }
15396             this.fireEvent('render', this);
15397
15398
15399             // Populate content of the component with html, contentEl or
15400             // a tpl.
15401             var contentTarget = this.getContentTarget();
15402             if (this.html){
15403                 contentTarget.update(Ext.DomHelper.markup(this.html));
15404                 delete this.html;
15405             }
15406             if (this.contentEl){
15407                 var ce = Ext.getDom(this.contentEl);
15408                 Ext.fly(ce).removeClass(['x-hidden', 'x-hide-display']);
15409                 contentTarget.appendChild(ce);
15410             }
15411             if (this.tpl) {
15412                 if (!this.tpl.compile) {
15413                     this.tpl = new Ext.XTemplate(this.tpl);
15414                 }
15415                 if (this.data) {
15416                     this.tpl[this.tplWriteMode](contentTarget, this.data);
15417                     delete this.data;
15418                 }
15419             }
15420             this.afterRender(this.container);
15421
15422
15423             if(this.hidden){
15424                 // call this so we don't fire initial hide events.
15425                 this.doHide();
15426             }
15427             if(this.disabled){
15428                 // pass silent so the event doesn't fire the first time.
15429                 this.disable(true);
15430             }
15431
15432             if(this.stateful !== false){
15433                 this.initStateEvents();
15434             }
15435             this.fireEvent('afterrender', this);
15436         }
15437         return this;
15438     },
15439
15440
15441     /**
15442      * Update the content area of a component.
15443      * @param {Mixed} htmlOrData
15444      * If this component has been configured with a template via the tpl config
15445      * then it will use this argument as data to populate the template.
15446      * If this component was not configured with a template, the components
15447      * content area will be updated via Ext.Element update
15448      * @param {Boolean} loadScripts
15449      * (optional) Only legitimate when using the html configuration. Defaults to false
15450      * @param {Function} callback
15451      * (optional) Only legitimate when using the html configuration. Callback to execute when scripts have finished loading
15452      */
15453     update: function(htmlOrData, loadScripts, cb) {
15454         var contentTarget = this.getContentTarget();
15455         if (this.tpl && typeof htmlOrData !== "string") {
15456             this.tpl[this.tplWriteMode](contentTarget, htmlOrData || {});
15457         } else {
15458             var html = Ext.isObject(htmlOrData) ? Ext.DomHelper.markup(htmlOrData) : htmlOrData;
15459             contentTarget.update(html, loadScripts, cb);
15460         }
15461     },
15462
15463
15464     /**
15465      * @private
15466      * Method to manage awareness of when components are added to their
15467      * respective Container, firing an added event.
15468      * References are established at add time rather than at render time.
15469      * @param {Ext.Container} container Container which holds the component
15470      * @param {number} pos Position at which the component was added
15471      */
15472     onAdded : function(container, pos) {
15473         this.ownerCt = container;
15474         this.initRef();
15475         this.fireEvent('added', this, container, pos);
15476     },
15477
15478     /**
15479      * @private
15480      * Method to manage awareness of when components are removed from their
15481      * respective Container, firing an removed event. References are properly
15482      * cleaned up after removing a component from its owning container.
15483      */
15484     onRemoved : function() {
15485         this.removeRef();
15486         this.fireEvent('removed', this, this.ownerCt);
15487         delete this.ownerCt;
15488     },
15489
15490     /**
15491      * @private
15492      * Method to establish a reference to a component.
15493      */
15494     initRef : function() {
15495         /**
15496          * @cfg {String} ref
15497          * <p>A path specification, relative to the Component's <code>{@link #ownerCt}</code>
15498          * specifying into which ancestor Container to place a named reference to this Component.</p>
15499          * <p>The ancestor axis can be traversed by using '/' characters in the path.
15500          * For example, to put a reference to a Toolbar Button into <i>the Panel which owns the Toolbar</i>:</p><pre><code>
15501 var myGrid = new Ext.grid.EditorGridPanel({
15502     title: 'My EditorGridPanel',
15503     store: myStore,
15504     colModel: myColModel,
15505     tbar: [{
15506         text: 'Save',
15507         handler: saveChanges,
15508         disabled: true,
15509         ref: '../saveButton'
15510     }],
15511     listeners: {
15512         afteredit: function() {
15513 //          The button reference is in the GridPanel
15514             myGrid.saveButton.enable();
15515         }
15516     }
15517 });
15518 </code></pre>
15519          * <p>In the code above, if the <code>ref</code> had been <code>'saveButton'</code>
15520          * the reference would have been placed into the Toolbar. Each '/' in the <code>ref</code>
15521          * moves up one level from the Component's <code>{@link #ownerCt}</code>.</p>
15522          * <p>Also see the <code>{@link #added}</code> and <code>{@link #removed}</code> events.</p>
15523          */
15524         if(this.ref && !this.refOwner){
15525             var levels = this.ref.split('/'),
15526                 last = levels.length,
15527                 i = 0,
15528                 t = this;
15529
15530             while(t && i < last){
15531                 t = t.ownerCt;
15532                 ++i;
15533             }
15534             if(t){
15535                 t[this.refName = levels[--i]] = this;
15536                 /**
15537                  * @type Ext.Container
15538                  * @property refOwner
15539                  * The ancestor Container into which the {@link #ref} reference was inserted if this Component
15540                  * is a child of a Container, and has been configured with a <code>ref</code>.
15541                  */
15542                 this.refOwner = t;
15543             }
15544         }
15545     },
15546
15547     removeRef : function() {
15548         if (this.refOwner && this.refName) {
15549             delete this.refOwner[this.refName];
15550             delete this.refOwner;
15551         }
15552     },
15553
15554     // private
15555     initState : function(){
15556         if(Ext.state.Manager){
15557             var id = this.getStateId();
15558             if(id){
15559                 var state = Ext.state.Manager.get(id);
15560                 if(state){
15561                     if(this.fireEvent('beforestaterestore', this, state) !== false){
15562                         this.applyState(Ext.apply({}, state));
15563                         this.fireEvent('staterestore', this, state);
15564                     }
15565                 }
15566             }
15567         }
15568     },
15569
15570     // private
15571     getStateId : function(){
15572         return this.stateId || ((/^(ext-comp-|ext-gen)/).test(String(this.id)) ? null : this.id);
15573     },
15574
15575     // private
15576     initStateEvents : function(){
15577         if(this.stateEvents){
15578             for(var i = 0, e; e = this.stateEvents[i]; i++){
15579                 this.on(e, this.saveState, this, {delay:100});
15580             }
15581         }
15582     },
15583
15584     // private
15585     applyState : function(state){
15586         if(state){
15587             Ext.apply(this, state);
15588         }
15589     },
15590
15591     // private
15592     getState : function(){
15593         return null;
15594     },
15595
15596     // private
15597     saveState : function(){
15598         if(Ext.state.Manager && this.stateful !== false){
15599             var id = this.getStateId();
15600             if(id){
15601                 var state = this.getState();
15602                 if(this.fireEvent('beforestatesave', this, state) !== false){
15603                     Ext.state.Manager.set(id, state);
15604                     this.fireEvent('statesave', this, state);
15605                 }
15606             }
15607         }
15608     },
15609
15610     /**
15611      * Apply this component to existing markup that is valid. With this function, no call to render() is required.
15612      * @param {String/HTMLElement} el
15613      */
15614     applyToMarkup : function(el){
15615         this.allowDomMove = false;
15616         this.el = Ext.get(el);
15617         this.render(this.el.dom.parentNode);
15618     },
15619
15620     /**
15621      * Adds a CSS class to the component's underlying element.
15622      * @param {string} cls The CSS class name to add
15623      * @return {Ext.Component} this
15624      */
15625     addClass : function(cls){
15626         if(this.el){
15627             this.el.addClass(cls);
15628         }else{
15629             this.cls = this.cls ? this.cls + ' ' + cls : cls;
15630         }
15631         return this;
15632     },
15633
15634     /**
15635      * Removes a CSS class from the component's underlying element.
15636      * @param {string} cls The CSS class name to remove
15637      * @return {Ext.Component} this
15638      */
15639     removeClass : function(cls){
15640         if(this.el){
15641             this.el.removeClass(cls);
15642         }else if(this.cls){
15643             this.cls = this.cls.split(' ').remove(cls).join(' ');
15644         }
15645         return this;
15646     },
15647
15648     // private
15649     // default function is not really useful
15650     onRender : function(ct, position){
15651         if(!this.el && this.autoEl){
15652             if(Ext.isString(this.autoEl)){
15653                 this.el = document.createElement(this.autoEl);
15654             }else{
15655                 var div = document.createElement('div');
15656                 Ext.DomHelper.overwrite(div, this.autoEl);
15657                 this.el = div.firstChild;
15658             }
15659             if (!this.el.id) {
15660                 this.el.id = this.getId();
15661             }
15662         }
15663         if(this.el){
15664             this.el = Ext.get(this.el);
15665             if(this.allowDomMove !== false){
15666                 ct.dom.insertBefore(this.el.dom, position);
15667                 if (div) {
15668                     Ext.removeNode(div);
15669                     div = null;
15670                 }
15671             }
15672         }
15673     },
15674
15675     // private
15676     getAutoCreate : function(){
15677         var cfg = Ext.isObject(this.autoCreate) ?
15678                       this.autoCreate : Ext.apply({}, this.defaultAutoCreate);
15679         if(this.id && !cfg.id){
15680             cfg.id = this.id;
15681         }
15682         return cfg;
15683     },
15684
15685     // private
15686     afterRender : Ext.emptyFn,
15687
15688     /**
15689      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
15690      * removing the component from its {@link Ext.Container} (if applicable) and unregistering it from
15691      * {@link Ext.ComponentMgr}.  Destruction is generally handled automatically by the framework and this method
15692      * should usually not need to be called directly.
15693      *
15694      */
15695     destroy : function(){
15696         if(!this.isDestroyed){
15697             if(this.fireEvent('beforedestroy', this) !== false){
15698                 this.destroying = true;
15699                 this.beforeDestroy();
15700                 if(this.ownerCt && this.ownerCt.remove){
15701                     this.ownerCt.remove(this, false);
15702                 }
15703                 if(this.rendered){
15704                     this.el.remove();
15705                     if(this.actionMode == 'container' || this.removeMode == 'container'){
15706                         this.container.remove();
15707                     }
15708                 }
15709                 this.onDestroy();
15710                 Ext.ComponentMgr.unregister(this);
15711                 this.fireEvent('destroy', this);
15712                 this.purgeListeners();
15713                 this.destroying = false;
15714                 this.isDestroyed = true;
15715             }
15716         }
15717     },
15718
15719     deleteMembers : function(){
15720         var args = arguments;
15721         for(var i = 0, len = args.length; i < len; ++i){
15722             delete this[args[i]];
15723         }
15724     },
15725
15726     // private
15727     beforeDestroy : Ext.emptyFn,
15728
15729     // private
15730     onDestroy  : Ext.emptyFn,
15731
15732     /**
15733      * <p>Returns the {@link Ext.Element} which encapsulates this Component.</p>
15734      * <p>This will <i>usually</i> be a &lt;DIV> element created by the class's onRender method, but
15735      * that may be overridden using the {@link #autoEl} config.</p>
15736      * <br><p><b>Note</b>: this element will not be available until this Component has been rendered.</p><br>
15737      * <p>To add listeners for <b>DOM events</b> to this Component (as opposed to listeners
15738      * for this Component's own Observable events), see the {@link #listeners} config for a suggestion,
15739      * or use a render listener directly:</p><pre><code>
15740 new Ext.Panel({
15741     title: 'The Clickable Panel',
15742     listeners: {
15743         render: function(p) {
15744             // Append the Panel to the click handler&#39;s argument list.
15745             p.getEl().on('click', handlePanelClick.createDelegate(null, [p], true));
15746         },
15747         single: true  // Remove the listener after first invocation
15748     }
15749 });
15750 </code></pre>
15751      * @return {Ext.Element} The Element which encapsulates this Component.
15752      */
15753     getEl : function(){
15754         return this.el;
15755     },
15756
15757     // private
15758     getContentTarget : function(){
15759         return this.el;
15760     },
15761
15762     /**
15763      * Returns the <code>id</code> of this component or automatically generates and
15764      * returns an <code>id</code> if an <code>id</code> is not defined yet:<pre><code>
15765      * 'ext-comp-' + (++Ext.Component.AUTO_ID)
15766      * </code></pre>
15767      * @return {String} id
15768      */
15769     getId : function(){
15770         return this.id || (this.id = 'ext-comp-' + (++Ext.Component.AUTO_ID));
15771     },
15772
15773     /**
15774      * Returns the <code>{@link #itemId}</code> of this component.  If an
15775      * <code>{@link #itemId}</code> was not assigned through configuration the
15776      * <code>id</code> is returned using <code>{@link #getId}</code>.
15777      * @return {String}
15778      */
15779     getItemId : function(){
15780         return this.itemId || this.getId();
15781     },
15782
15783     /**
15784      * Try to focus this component.
15785      * @param {Boolean} selectText (optional) If applicable, true to also select the text in this component
15786      * @param {Boolean/Number} delay (optional) Delay the focus this number of milliseconds (true for 10 milliseconds)
15787      * @return {Ext.Component} this
15788      */
15789     focus : function(selectText, delay){
15790         if(delay){
15791             this.focus.defer(Ext.isNumber(delay) ? delay : 10, this, [selectText, false]);
15792             return;
15793         }
15794         if(this.rendered){
15795             this.el.focus();
15796             if(selectText === true){
15797                 this.el.dom.select();
15798             }
15799         }
15800         return this;
15801     },
15802
15803     // private
15804     blur : function(){
15805         if(this.rendered){
15806             this.el.blur();
15807         }
15808         return this;
15809     },
15810
15811     /**
15812      * Disable this component and fire the 'disable' event.
15813      * @return {Ext.Component} this
15814      */
15815     disable : function(/* private */ silent){
15816         if(this.rendered){
15817             this.onDisable();
15818         }
15819         this.disabled = true;
15820         if(silent !== true){
15821             this.fireEvent('disable', this);
15822         }
15823         return this;
15824     },
15825
15826     // private
15827     onDisable : function(){
15828         this.getActionEl().addClass(this.disabledClass);
15829         this.el.dom.disabled = true;
15830     },
15831
15832     /**
15833      * Enable this component and fire the 'enable' event.
15834      * @return {Ext.Component} this
15835      */
15836     enable : function(){
15837         if(this.rendered){
15838             this.onEnable();
15839         }
15840         this.disabled = false;
15841         this.fireEvent('enable', this);
15842         return this;
15843     },
15844
15845     // private
15846     onEnable : function(){
15847         this.getActionEl().removeClass(this.disabledClass);
15848         this.el.dom.disabled = false;
15849     },
15850
15851     /**
15852      * Convenience function for setting disabled/enabled by boolean.
15853      * @param {Boolean} disabled
15854      * @return {Ext.Component} this
15855      */
15856     setDisabled : function(disabled){
15857         return this[disabled ? 'disable' : 'enable']();
15858     },
15859
15860     /**
15861      * Show this component.  Listen to the '{@link #beforeshow}' event and return
15862      * <tt>false</tt> to cancel showing the component.  Fires the '{@link #show}'
15863      * event after showing the component.
15864      * @return {Ext.Component} this
15865      */
15866     show : function(){
15867         if(this.fireEvent('beforeshow', this) !== false){
15868             this.hidden = false;
15869             if(this.autoRender){
15870                 this.render(Ext.isBoolean(this.autoRender) ? Ext.getBody() : this.autoRender);
15871             }
15872             if(this.rendered){
15873                 this.onShow();
15874             }
15875             this.fireEvent('show', this);
15876         }
15877         return this;
15878     },
15879
15880     // private
15881     onShow : function(){
15882         this.getVisibilityEl().removeClass('x-hide-' + this.hideMode);
15883     },
15884
15885     /**
15886      * Hide this component.  Listen to the '{@link #beforehide}' event and return
15887      * <tt>false</tt> to cancel hiding the component.  Fires the '{@link #hide}'
15888      * event after hiding the component. Note this method is called internally if
15889      * the component is configured to be <code>{@link #hidden}</code>.
15890      * @return {Ext.Component} this
15891      */
15892     hide : function(){
15893         if(this.fireEvent('beforehide', this) !== false){
15894             this.doHide();
15895             this.fireEvent('hide', this);
15896         }
15897         return this;
15898     },
15899
15900     // private
15901     doHide: function(){
15902         this.hidden = true;
15903         if(this.rendered){
15904             this.onHide();
15905         }
15906     },
15907
15908     // private
15909     onHide : function(){
15910         this.getVisibilityEl().addClass('x-hide-' + this.hideMode);
15911     },
15912
15913     // private
15914     getVisibilityEl : function(){
15915         return this.hideParent ? this.container : this.getActionEl();
15916     },
15917
15918     /**
15919      * Convenience function to hide or show this component by boolean.
15920      * @param {Boolean} visible True to show, false to hide
15921      * @return {Ext.Component} this
15922      */
15923     setVisible : function(visible){
15924         return this[visible ? 'show' : 'hide']();
15925     },
15926
15927     /**
15928      * Returns true if this component is visible.
15929      * @return {Boolean} True if this component is visible, false otherwise.
15930      */
15931     isVisible : function(){
15932         return this.rendered && this.getVisibilityEl().isVisible();
15933     },
15934
15935     /**
15936      * Clone the current component using the original config values passed into this instance by default.
15937      * @param {Object} overrides A new config containing any properties to override in the cloned version.
15938      * An id property can be passed on this object, otherwise one will be generated to avoid duplicates.
15939      * @return {Ext.Component} clone The cloned copy of this component
15940      */
15941     cloneConfig : function(overrides){
15942         overrides = overrides || {};
15943         var id = overrides.id || Ext.id();
15944         var cfg = Ext.applyIf(overrides, this.initialConfig);
15945         cfg.id = id; // prevent dup id
15946         return new this.constructor(cfg);
15947     },
15948
15949     /**
15950      * Gets the xtype for this component as registered with {@link Ext.ComponentMgr}. For a list of all
15951      * available xtypes, see the {@link Ext.Component} header. Example usage:
15952      * <pre><code>
15953 var t = new Ext.form.TextField();
15954 alert(t.getXType());  // alerts 'textfield'
15955 </code></pre>
15956      * @return {String} The xtype
15957      */
15958     getXType : function(){
15959         return this.constructor.xtype;
15960     },
15961
15962     /**
15963      * <p>Tests whether or not this Component is of a specific xtype. This can test whether this Component is descended
15964      * from the xtype (default) or whether it is directly of the xtype specified (shallow = true).</p>
15965      * <p><b>If using your own subclasses, be aware that a Component must register its own xtype
15966      * to participate in determination of inherited xtypes.</b></p>
15967      * <p>For a list of all available xtypes, see the {@link Ext.Component} header.</p>
15968      * <p>Example usage:</p>
15969      * <pre><code>
15970 var t = new Ext.form.TextField();
15971 var isText = t.isXType('textfield');        // true
15972 var isBoxSubclass = t.isXType('box');       // true, descended from BoxComponent
15973 var isBoxInstance = t.isXType('box', true); // false, not a direct BoxComponent instance
15974 </code></pre>
15975      * @param {String} xtype The xtype to check for this Component
15976      * @param {Boolean} shallow (optional) False to check whether this Component is descended from the xtype (this is
15977      * the default), or true to check whether this Component is directly of the specified xtype.
15978      * @return {Boolean} True if this component descends from the specified xtype, false otherwise.
15979      */
15980     isXType : function(xtype, shallow){
15981         //assume a string by default
15982         if (Ext.isFunction(xtype)){
15983             xtype = xtype.xtype; //handle being passed the class, e.g. Ext.Component
15984         }else if (Ext.isObject(xtype)){
15985             xtype = xtype.constructor.xtype; //handle being passed an instance
15986         }
15987
15988         return !shallow ? ('/' + this.getXTypes() + '/').indexOf('/' + xtype + '/') != -1 : this.constructor.xtype == xtype;
15989     },
15990
15991     /**
15992      * <p>Returns this Component's xtype hierarchy as a slash-delimited string. For a list of all
15993      * available xtypes, see the {@link Ext.Component} header.</p>
15994      * <p><b>If using your own subclasses, be aware that a Component must register its own xtype
15995      * to participate in determination of inherited xtypes.</b></p>
15996      * <p>Example usage:</p>
15997      * <pre><code>
15998 var t = new Ext.form.TextField();
15999 alert(t.getXTypes());  // alerts 'component/box/field/textfield'
16000 </code></pre>
16001      * @return {String} The xtype hierarchy string
16002      */
16003     getXTypes : function(){
16004         var tc = this.constructor;
16005         if(!tc.xtypes){
16006             var c = [], sc = this;
16007             while(sc && sc.constructor.xtype){
16008                 c.unshift(sc.constructor.xtype);
16009                 sc = sc.constructor.superclass;
16010             }
16011             tc.xtypeChain = c;
16012             tc.xtypes = c.join('/');
16013         }
16014         return tc.xtypes;
16015     },
16016
16017     /**
16018      * Find a container above this component at any level by a custom function. If the passed function returns
16019      * true, the container will be returned.
16020      * @param {Function} fn The custom function to call with the arguments (container, this component).
16021      * @return {Ext.Container} The first Container for which the custom function returns true
16022      */
16023     findParentBy : function(fn) {
16024         for (var p = this.ownerCt; (p != null) && !fn(p, this); p = p.ownerCt);
16025         return p || null;
16026     },
16027
16028     /**
16029      * Find a container above this component at any level by xtype or class
16030      * @param {String/Class} xtype The xtype string for a component, or the class of the component directly
16031      * @return {Ext.Container} The first Container which matches the given xtype or class
16032      */
16033     findParentByType : function(xtype) {
16034         return Ext.isFunction(xtype) ?
16035             this.findParentBy(function(p){
16036                 return p.constructor === xtype;
16037             }) :
16038             this.findParentBy(function(p){
16039                 return p.constructor.xtype === xtype;
16040             });
16041     },
16042
16043     // protected
16044     getPositionEl : function(){
16045         return this.positionEl || this.el;
16046     },
16047
16048     // private
16049     purgeListeners : function(){
16050         Ext.Component.superclass.purgeListeners.call(this);
16051         if(this.mons){
16052             this.on('beforedestroy', this.clearMons, this, {single: true});
16053         }
16054     },
16055
16056     // private
16057     clearMons : function(){
16058         Ext.each(this.mons, function(m){
16059             m.item.un(m.ename, m.fn, m.scope);
16060         }, this);
16061         this.mons = [];
16062     },
16063
16064     // private
16065     createMons: function(){
16066         if(!this.mons){
16067             this.mons = [];
16068             this.on('beforedestroy', this.clearMons, this, {single: true});
16069         }
16070     },
16071
16072     /**
16073      * <p>Adds listeners to any Observable object (or Elements) which are automatically removed when this Component
16074      * is destroyed. Usage:</p><code><pre>
16075 myGridPanel.mon(myGridPanel.getSelectionModel(), 'selectionchange', handleSelectionChange, null, {buffer: 50});
16076 </pre></code>
16077      * <p>or:</p><code><pre>
16078 myGridPanel.mon(myGridPanel.getSelectionModel(), {
16079     selectionchange: handleSelectionChange,
16080     buffer: 50
16081 });
16082 </pre></code>
16083      * @param {Observable|Element} item The item to which to add a listener/listeners.
16084      * @param {Object|String} ename The event name, or an object containing event name properties.
16085      * @param {Function} fn Optional. If the <code>ename</code> parameter was an event name, this
16086      * is the handler function.
16087      * @param {Object} scope Optional. If the <code>ename</code> parameter was an event name, this
16088      * is the scope (<code>this</code> reference) in which the handler function is executed.
16089      * @param {Object} opt Optional. If the <code>ename</code> parameter was an event name, this
16090      * is the {@link Ext.util.Observable#addListener addListener} options.
16091      */
16092     mon : function(item, ename, fn, scope, opt){
16093         this.createMons();
16094         if(Ext.isObject(ename)){
16095             var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
16096
16097             var o = ename;
16098             for(var e in o){
16099                 if(propRe.test(e)){
16100                     continue;
16101                 }
16102                 if(Ext.isFunction(o[e])){
16103                     // shared options
16104                     this.mons.push({
16105                         item: item, ename: e, fn: o[e], scope: o.scope
16106                     });
16107                     item.on(e, o[e], o.scope, o);
16108                 }else{
16109                     // individual options
16110                     this.mons.push({
16111                         item: item, ename: e, fn: o[e], scope: o.scope
16112                     });
16113                     item.on(e, o[e]);
16114                 }
16115             }
16116             return;
16117         }
16118
16119         this.mons.push({
16120             item: item, ename: ename, fn: fn, scope: scope
16121         });
16122         item.on(ename, fn, scope, opt);
16123     },
16124
16125     /**
16126      * Removes listeners that were added by the {@link #mon} method.
16127      * @param {Observable|Element} item The item from which to remove a listener/listeners.
16128      * @param {Object|String} ename The event name, or an object containing event name properties.
16129      * @param {Function} fn Optional. If the <code>ename</code> parameter was an event name, this
16130      * is the handler function.
16131      * @param {Object} scope Optional. If the <code>ename</code> parameter was an event name, this
16132      * is the scope (<code>this</code> reference) in which the handler function is executed.
16133      */
16134     mun : function(item, ename, fn, scope){
16135         var found, mon;
16136         this.createMons();
16137         for(var i = 0, len = this.mons.length; i < len; ++i){
16138             mon = this.mons[i];
16139             if(item === mon.item && ename == mon.ename && fn === mon.fn && scope === mon.scope){
16140                 this.mons.splice(i, 1);
16141                 item.un(ename, fn, scope);
16142                 found = true;
16143                 break;
16144             }
16145         }
16146         return found;
16147     },
16148
16149     /**
16150      * Returns the next component in the owning container
16151      * @return Ext.Component
16152      */
16153     nextSibling : function(){
16154         if(this.ownerCt){
16155             var index = this.ownerCt.items.indexOf(this);
16156             if(index != -1 && index+1 < this.ownerCt.items.getCount()){
16157                 return this.ownerCt.items.itemAt(index+1);
16158             }
16159         }
16160         return null;
16161     },
16162
16163     /**
16164      * Returns the previous component in the owning container
16165      * @return Ext.Component
16166      */
16167     previousSibling : function(){
16168         if(this.ownerCt){
16169             var index = this.ownerCt.items.indexOf(this);
16170             if(index > 0){
16171                 return this.ownerCt.items.itemAt(index-1);
16172             }
16173         }
16174         return null;
16175     },
16176
16177     /**
16178      * Provides the link for Observable's fireEvent method to bubble up the ownership hierarchy.
16179      * @return {Ext.Container} the Container which owns this Component.
16180      */
16181     getBubbleTarget : function(){
16182         return this.ownerCt;
16183     }
16184 });
16185
16186 Ext.reg('component', Ext.Component);/**\r
16187  * @class Ext.Action\r
16188  * <p>An Action is a piece of reusable functionality that can be abstracted out of any particular component so that it\r
16189  * can be usefully shared among multiple components.  Actions let you share handlers, configuration options and UI\r
16190  * updates across any components that support the Action interface (primarily {@link Ext.Toolbar}, {@link Ext.Button}\r
16191  * and {@link Ext.menu.Menu} components).</p>\r
16192  * <p>Aside from supporting the config object interface, any component that needs to use Actions must also support\r
16193  * the following method list, as these will be called as needed by the Action class: setText(string), setIconCls(string),\r
16194  * setDisabled(boolean), setVisible(boolean) and setHandler(function).</p>\r
16195  * Example usage:<br>\r
16196  * <pre><code>\r
16197 // Define the shared action.  Each component below will have the same\r
16198 // display text and icon, and will display the same message on click.\r
16199 var action = new Ext.Action({\r
16200     {@link #text}: 'Do something',\r
16201     {@link #handler}: function(){\r
16202         Ext.Msg.alert('Click', 'You did something.');\r
16203     },\r
16204     {@link #iconCls}: 'do-something',\r
16205     {@link #itemId}: 'myAction'\r
16206 });\r
16207 \r
16208 var panel = new Ext.Panel({\r
16209     title: 'Actions',\r
16210     width: 500,\r
16211     height: 300,\r
16212     tbar: [\r
16213         // Add the action directly to a toolbar as a menu button\r
16214         action,\r
16215         {\r
16216             text: 'Action Menu',\r
16217             // Add the action to a menu as a text item\r
16218             menu: [action]\r
16219         }\r
16220     ],\r
16221     items: [\r
16222         // Add the action to the panel body as a standard button\r
16223         new Ext.Button(action)\r
16224     ],\r
16225     renderTo: Ext.getBody()\r
16226 });\r
16227 \r
16228 // Change the text for all components using the action\r
16229 action.setText('Something else');\r
16230 \r
16231 // Reference an action through a container using the itemId\r
16232 var btn = panel.getComponent('myAction');\r
16233 var aRef = btn.baseAction;\r
16234 aRef.setText('New text');\r
16235 </code></pre>\r
16236  * @constructor\r
16237  * @param {Object} config The configuration options\r
16238  */\r
16239 Ext.Action = Ext.extend(Object, {\r
16240     /**\r
16241      * @cfg {String} text The text to set for all components using this action (defaults to '').\r
16242      */\r
16243     /**\r
16244      * @cfg {String} iconCls\r
16245      * The CSS class selector that specifies a background image to be used as the header icon for\r
16246      * all components using this action (defaults to '').\r
16247      * <p>An example of specifying a custom icon class would be something like:\r
16248      * </p><pre><code>\r
16249 // specify the property in the config for the class:\r
16250      ...\r
16251      iconCls: 'do-something'\r
16252 \r
16253 // css class that specifies background image to be used as the icon image:\r
16254 .do-something { background-image: url(../images/my-icon.gif) 0 6px no-repeat !important; }\r
16255 </code></pre>\r
16256      */\r
16257     /**\r
16258      * @cfg {Boolean} disabled True to disable all components using this action, false to enable them (defaults to false).\r
16259      */\r
16260     /**\r
16261      * @cfg {Boolean} hidden True to hide all components using this action, false to show them (defaults to false).\r
16262      */\r
16263     /**\r
16264      * @cfg {Function} handler The function that will be invoked by each component tied to this action\r
16265      * when the component's primary event is triggered (defaults to undefined).\r
16266      */\r
16267     /**\r
16268      * @cfg {String} itemId\r
16269      * See {@link Ext.Component}.{@link Ext.Component#itemId itemId}.\r
16270      */\r
16271     /**\r
16272      * @cfg {Object} scope The scope (<tt><b>this</b></tt> reference) in which the\r
16273      * <code>{@link #handler}</code> is executed. Defaults to this Button.\r
16274      */\r
16275 \r
16276     constructor : function(config){\r
16277         this.initialConfig = config;\r
16278         this.itemId = config.itemId = (config.itemId || config.id || Ext.id());\r
16279         this.items = [];\r
16280     },\r
16281     \r
16282     // private\r
16283     isAction : true,\r
16284 \r
16285     /**\r
16286      * Sets the text to be displayed by all components using this action.\r
16287      * @param {String} text The text to display\r
16288      */\r
16289     setText : function(text){\r
16290         this.initialConfig.text = text;\r
16291         this.callEach('setText', [text]);\r
16292     },\r
16293 \r
16294     /**\r
16295      * Gets the text currently displayed by all components using this action.\r
16296      */\r
16297     getText : function(){\r
16298         return this.initialConfig.text;\r
16299     },\r
16300 \r
16301     /**\r
16302      * Sets the icon CSS class for all components using this action.  The class should supply\r
16303      * a background image that will be used as the icon image.\r
16304      * @param {String} cls The CSS class supplying the icon image\r
16305      */\r
16306     setIconClass : function(cls){\r
16307         this.initialConfig.iconCls = cls;\r
16308         this.callEach('setIconClass', [cls]);\r
16309     },\r
16310 \r
16311     /**\r
16312      * Gets the icon CSS class currently used by all components using this action.\r
16313      */\r
16314     getIconClass : function(){\r
16315         return this.initialConfig.iconCls;\r
16316     },\r
16317 \r
16318     /**\r
16319      * Sets the disabled state of all components using this action.  Shortcut method\r
16320      * for {@link #enable} and {@link #disable}.\r
16321      * @param {Boolean} disabled True to disable the component, false to enable it\r
16322      */\r
16323     setDisabled : function(v){\r
16324         this.initialConfig.disabled = v;\r
16325         this.callEach('setDisabled', [v]);\r
16326     },\r
16327 \r
16328     /**\r
16329      * Enables all components using this action.\r
16330      */\r
16331     enable : function(){\r
16332         this.setDisabled(false);\r
16333     },\r
16334 \r
16335     /**\r
16336      * Disables all components using this action.\r
16337      */\r
16338     disable : function(){\r
16339         this.setDisabled(true);\r
16340     },\r
16341 \r
16342     /**\r
16343      * Returns true if the components using this action are currently disabled, else returns false.  \r
16344      */\r
16345     isDisabled : function(){\r
16346         return this.initialConfig.disabled;\r
16347     },\r
16348 \r
16349     /**\r
16350      * Sets the hidden state of all components using this action.  Shortcut method\r
16351      * for <code>{@link #hide}</code> and <code>{@link #show}</code>.\r
16352      * @param {Boolean} hidden True to hide the component, false to show it\r
16353      */\r
16354     setHidden : function(v){\r
16355         this.initialConfig.hidden = v;\r
16356         this.callEach('setVisible', [!v]);\r
16357     },\r
16358 \r
16359     /**\r
16360      * Shows all components using this action.\r
16361      */\r
16362     show : function(){\r
16363         this.setHidden(false);\r
16364     },\r
16365 \r
16366     /**\r
16367      * Hides all components using this action.\r
16368      */\r
16369     hide : function(){\r
16370         this.setHidden(true);\r
16371     },\r
16372 \r
16373     /**\r
16374      * Returns true if the components using this action are currently hidden, else returns false.  \r
16375      */\r
16376     isHidden : function(){\r
16377         return this.initialConfig.hidden;\r
16378     },\r
16379 \r
16380     /**\r
16381      * Sets the function that will be called by each Component using this action when its primary event is triggered.\r
16382      * @param {Function} fn The function that will be invoked by the action's components.  The function\r
16383      * will be called with no arguments.\r
16384      * @param {Object} scope The scope (<code>this</code> reference) in which the function is executed. Defaults to the Component firing the event.\r
16385      */\r
16386     setHandler : function(fn, scope){\r
16387         this.initialConfig.handler = fn;\r
16388         this.initialConfig.scope = scope;\r
16389         this.callEach('setHandler', [fn, scope]);\r
16390     },\r
16391 \r
16392     /**\r
16393      * Executes the specified function once for each Component currently tied to this action.  The function passed\r
16394      * in should accept a single argument that will be an object that supports the basic Action config/method interface.\r
16395      * @param {Function} fn The function to execute for each component\r
16396      * @param {Object} scope The scope (<code>this</code> reference) in which the function is executed.  Defaults to the Component.\r
16397      */\r
16398     each : function(fn, scope){\r
16399         Ext.each(this.items, fn, scope);\r
16400     },\r
16401 \r
16402     // private\r
16403     callEach : function(fnName, args){\r
16404         var cs = this.items;\r
16405         for(var i = 0, len = cs.length; i < len; i++){\r
16406             cs[i][fnName].apply(cs[i], args);\r
16407         }\r
16408     },\r
16409 \r
16410     // private\r
16411     addComponent : function(comp){\r
16412         this.items.push(comp);\r
16413         comp.on('destroy', this.removeComponent, this);\r
16414     },\r
16415 \r
16416     // private\r
16417     removeComponent : function(comp){\r
16418         this.items.remove(comp);\r
16419     },\r
16420 \r
16421     /**\r
16422      * Executes this action manually using the handler function specified in the original config object\r
16423      * or the handler function set with <code>{@link #setHandler}</code>.  Any arguments passed to this\r
16424      * function will be passed on to the handler function.\r
16425      * @param {Mixed} arg1 (optional) Variable number of arguments passed to the handler function\r
16426      * @param {Mixed} arg2 (optional)\r
16427      * @param {Mixed} etc... (optional)\r
16428      */\r
16429     execute : function(){\r
16430         this.initialConfig.handler.apply(this.initialConfig.scope || window, arguments);\r
16431     }\r
16432 });\r
16433 /**
16434  * @class Ext.Layer
16435  * @extends Ext.Element
16436  * An extended {@link Ext.Element} object that supports a shadow and shim, constrain to viewport and
16437  * automatic maintaining of shadow/shim positions.
16438  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
16439  * @cfg {String/Boolean} shadow True to automatically create an {@link Ext.Shadow}, or a string indicating the
16440  * shadow's display {@link Ext.Shadow#mode}. False to disable the shadow. (defaults to false)
16441  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: 'div', cls: 'x-layer'}).
16442  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
16443  * @cfg {String} cls CSS class to add to the element
16444  * @cfg {Number} zindex Starting z-index (defaults to 11000)
16445  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 4)
16446  * @cfg {Boolean} useDisplay
16447  * Defaults to use css offsets to hide the Layer. Specify <tt>true</tt>
16448  * to use css style <tt>'display:none;'</tt> to hide the Layer.
16449  * @constructor
16450  * @param {Object} config An object with config options.
16451  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
16452  */
16453 (function(){
16454 Ext.Layer = function(config, existingEl){
16455     config = config || {};
16456     var dh = Ext.DomHelper;
16457     var cp = config.parentEl, pel = cp ? Ext.getDom(cp) : document.body;
16458     if(existingEl){
16459         this.dom = Ext.getDom(existingEl);
16460     }
16461     if(!this.dom){
16462         var o = config.dh || {tag: 'div', cls: 'x-layer'};
16463         this.dom = dh.append(pel, o);
16464     }
16465     if(config.cls){
16466         this.addClass(config.cls);
16467     }
16468     this.constrain = config.constrain !== false;
16469     this.setVisibilityMode(Ext.Element.VISIBILITY);
16470     if(config.id){
16471         this.id = this.dom.id = config.id;
16472     }else{
16473         this.id = Ext.id(this.dom);
16474     }
16475     this.zindex = config.zindex || this.getZIndex();
16476     this.position('absolute', this.zindex);
16477     if(config.shadow){
16478         this.shadowOffset = config.shadowOffset || 4;
16479         this.shadow = new Ext.Shadow({
16480             offset : this.shadowOffset,
16481             mode : config.shadow
16482         });
16483     }else{
16484         this.shadowOffset = 0;
16485     }
16486     this.useShim = config.shim !== false && Ext.useShims;
16487     this.useDisplay = config.useDisplay;
16488     this.hide();
16489 };
16490
16491 var supr = Ext.Element.prototype;
16492
16493 // shims are shared among layer to keep from having 100 iframes
16494 var shims = [];
16495
16496 Ext.extend(Ext.Layer, Ext.Element, {
16497
16498     getZIndex : function(){
16499         return this.zindex || parseInt((this.getShim() || this).getStyle('z-index'), 10) || 11000;
16500     },
16501
16502     getShim : function(){
16503         if(!this.useShim){
16504             return null;
16505         }
16506         if(this.shim){
16507             return this.shim;
16508         }
16509         var shim = shims.shift();
16510         if(!shim){
16511             shim = this.createShim();
16512             shim.enableDisplayMode('block');
16513             shim.dom.style.display = 'none';
16514             shim.dom.style.visibility = 'visible';
16515         }
16516         var pn = this.dom.parentNode;
16517         if(shim.dom.parentNode != pn){
16518             pn.insertBefore(shim.dom, this.dom);
16519         }
16520         shim.setStyle('z-index', this.getZIndex()-2);
16521         this.shim = shim;
16522         return shim;
16523     },
16524
16525     hideShim : function(){
16526         if(this.shim){
16527             this.shim.setDisplayed(false);
16528             shims.push(this.shim);
16529             delete this.shim;
16530         }
16531     },
16532
16533     disableShadow : function(){
16534         if(this.shadow){
16535             this.shadowDisabled = true;
16536             this.shadow.hide();
16537             this.lastShadowOffset = this.shadowOffset;
16538             this.shadowOffset = 0;
16539         }
16540     },
16541
16542     enableShadow : function(show){
16543         if(this.shadow){
16544             this.shadowDisabled = false;
16545             this.shadowOffset = this.lastShadowOffset;
16546             delete this.lastShadowOffset;
16547             if(show){
16548                 this.sync(true);
16549             }
16550         }
16551     },
16552
16553     // private
16554     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
16555     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
16556     sync : function(doShow){
16557         var sw = this.shadow;
16558         if(!this.updating && this.isVisible() && (sw || this.useShim)){
16559             var sh = this.getShim();
16560
16561             var w = this.getWidth(),
16562                 h = this.getHeight();
16563
16564             var l = this.getLeft(true),
16565                 t = this.getTop(true);
16566
16567             if(sw && !this.shadowDisabled){
16568                 if(doShow && !sw.isVisible()){
16569                     sw.show(this);
16570                 }else{
16571                     sw.realign(l, t, w, h);
16572                 }
16573                 if(sh){
16574                     if(doShow){
16575                        sh.show();
16576                     }
16577                     // fit the shim behind the shadow, so it is shimmed too
16578                     var a = sw.adjusts, s = sh.dom.style;
16579                     s.left = (Math.min(l, l+a.l))+'px';
16580                     s.top = (Math.min(t, t+a.t))+'px';
16581                     s.width = (w+a.w)+'px';
16582                     s.height = (h+a.h)+'px';
16583                 }
16584             }else if(sh){
16585                 if(doShow){
16586                    sh.show();
16587                 }
16588                 sh.setSize(w, h);
16589                 sh.setLeftTop(l, t);
16590             }
16591
16592         }
16593     },
16594
16595     // private
16596     destroy : function(){
16597         this.hideShim();
16598         if(this.shadow){
16599             this.shadow.hide();
16600         }
16601         this.removeAllListeners();
16602         Ext.removeNode(this.dom);
16603         delete this.dom;
16604     },
16605
16606     remove : function(){
16607         this.destroy();
16608     },
16609
16610     // private
16611     beginUpdate : function(){
16612         this.updating = true;
16613     },
16614
16615     // private
16616     endUpdate : function(){
16617         this.updating = false;
16618         this.sync(true);
16619     },
16620
16621     // private
16622     hideUnders : function(negOffset){
16623         if(this.shadow){
16624             this.shadow.hide();
16625         }
16626         this.hideShim();
16627     },
16628
16629     // private
16630     constrainXY : function(){
16631         if(this.constrain){
16632             var vw = Ext.lib.Dom.getViewWidth(),
16633                 vh = Ext.lib.Dom.getViewHeight();
16634             var s = Ext.getDoc().getScroll();
16635
16636             var xy = this.getXY();
16637             var x = xy[0], y = xy[1];
16638             var so = this.shadowOffset;
16639             var w = this.dom.offsetWidth+so, h = this.dom.offsetHeight+so;
16640             // only move it if it needs it
16641             var moved = false;
16642             // first validate right/bottom
16643             if((x + w) > vw+s.left){
16644                 x = vw - w - so;
16645                 moved = true;
16646             }
16647             if((y + h) > vh+s.top){
16648                 y = vh - h - so;
16649                 moved = true;
16650             }
16651             // then make sure top/left isn't negative
16652             if(x < s.left){
16653                 x = s.left;
16654                 moved = true;
16655             }
16656             if(y < s.top){
16657                 y = s.top;
16658                 moved = true;
16659             }
16660             if(moved){
16661                 if(this.avoidY){
16662                     var ay = this.avoidY;
16663                     if(y <= ay && (y+h) >= ay){
16664                         y = ay-h-5;
16665                     }
16666                 }
16667                 xy = [x, y];
16668                 this.storeXY(xy);
16669                 supr.setXY.call(this, xy);
16670                 this.sync();
16671             }
16672         }
16673         return this;
16674     },
16675
16676     isVisible : function(){
16677         return this.visible;
16678     },
16679
16680     // private
16681     showAction : function(){
16682         this.visible = true; // track visibility to prevent getStyle calls
16683         if(this.useDisplay === true){
16684             this.setDisplayed('');
16685         }else if(this.lastXY){
16686             supr.setXY.call(this, this.lastXY);
16687         }else if(this.lastLT){
16688             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
16689         }
16690     },
16691
16692     // private
16693     hideAction : function(){
16694         this.visible = false;
16695         if(this.useDisplay === true){
16696             this.setDisplayed(false);
16697         }else{
16698             this.setLeftTop(-10000,-10000);
16699         }
16700     },
16701
16702     // overridden Element method
16703     setVisible : function(v, a, d, c, e){
16704         if(v){
16705             this.showAction();
16706         }
16707         if(a && v){
16708             var cb = function(){
16709                 this.sync(true);
16710                 if(c){
16711                     c();
16712                 }
16713             }.createDelegate(this);
16714             supr.setVisible.call(this, true, true, d, cb, e);
16715         }else{
16716             if(!v){
16717                 this.hideUnders(true);
16718             }
16719             var cb = c;
16720             if(a){
16721                 cb = function(){
16722                     this.hideAction();
16723                     if(c){
16724                         c();
16725                     }
16726                 }.createDelegate(this);
16727             }
16728             supr.setVisible.call(this, v, a, d, cb, e);
16729             if(v){
16730                 this.sync(true);
16731             }else if(!a){
16732                 this.hideAction();
16733             }
16734         }
16735         return this;
16736     },
16737
16738     storeXY : function(xy){
16739         delete this.lastLT;
16740         this.lastXY = xy;
16741     },
16742
16743     storeLeftTop : function(left, top){
16744         delete this.lastXY;
16745         this.lastLT = [left, top];
16746     },
16747
16748     // private
16749     beforeFx : function(){
16750         this.beforeAction();
16751         return Ext.Layer.superclass.beforeFx.apply(this, arguments);
16752     },
16753
16754     // private
16755     afterFx : function(){
16756         Ext.Layer.superclass.afterFx.apply(this, arguments);
16757         this.sync(this.isVisible());
16758     },
16759
16760     // private
16761     beforeAction : function(){
16762         if(!this.updating && this.shadow){
16763             this.shadow.hide();
16764         }
16765     },
16766
16767     // overridden Element method
16768     setLeft : function(left){
16769         this.storeLeftTop(left, this.getTop(true));
16770         supr.setLeft.apply(this, arguments);
16771         this.sync();
16772         return this;
16773     },
16774
16775     setTop : function(top){
16776         this.storeLeftTop(this.getLeft(true), top);
16777         supr.setTop.apply(this, arguments);
16778         this.sync();
16779         return this;
16780     },
16781
16782     setLeftTop : function(left, top){
16783         this.storeLeftTop(left, top);
16784         supr.setLeftTop.apply(this, arguments);
16785         this.sync();
16786         return this;
16787     },
16788
16789     setXY : function(xy, a, d, c, e){
16790         this.fixDisplay();
16791         this.beforeAction();
16792         this.storeXY(xy);
16793         var cb = this.createCB(c);
16794         supr.setXY.call(this, xy, a, d, cb, e);
16795         if(!a){
16796             cb();
16797         }
16798         return this;
16799     },
16800
16801     // private
16802     createCB : function(c){
16803         var el = this;
16804         return function(){
16805             el.constrainXY();
16806             el.sync(true);
16807             if(c){
16808                 c();
16809             }
16810         };
16811     },
16812
16813     // overridden Element method
16814     setX : function(x, a, d, c, e){
16815         this.setXY([x, this.getY()], a, d, c, e);
16816         return this;
16817     },
16818
16819     // overridden Element method
16820     setY : function(y, a, d, c, e){
16821         this.setXY([this.getX(), y], a, d, c, e);
16822         return this;
16823     },
16824
16825     // overridden Element method
16826     setSize : function(w, h, a, d, c, e){
16827         this.beforeAction();
16828         var cb = this.createCB(c);
16829         supr.setSize.call(this, w, h, a, d, cb, e);
16830         if(!a){
16831             cb();
16832         }
16833         return this;
16834     },
16835
16836     // overridden Element method
16837     setWidth : function(w, a, d, c, e){
16838         this.beforeAction();
16839         var cb = this.createCB(c);
16840         supr.setWidth.call(this, w, a, d, cb, e);
16841         if(!a){
16842             cb();
16843         }
16844         return this;
16845     },
16846
16847     // overridden Element method
16848     setHeight : function(h, a, d, c, e){
16849         this.beforeAction();
16850         var cb = this.createCB(c);
16851         supr.setHeight.call(this, h, a, d, cb, e);
16852         if(!a){
16853             cb();
16854         }
16855         return this;
16856     },
16857
16858     // overridden Element method
16859     setBounds : function(x, y, w, h, a, d, c, e){
16860         this.beforeAction();
16861         var cb = this.createCB(c);
16862         if(!a){
16863             this.storeXY([x, y]);
16864             supr.setXY.call(this, [x, y]);
16865             supr.setSize.call(this, w, h, a, d, cb, e);
16866             cb();
16867         }else{
16868             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
16869         }
16870         return this;
16871     },
16872
16873     /**
16874      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
16875      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
16876      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
16877      * @param {Number} zindex The new z-index to set
16878      * @return {this} The Layer
16879      */
16880     setZIndex : function(zindex){
16881         this.zindex = zindex;
16882         this.setStyle('z-index', zindex + 2);
16883         if(this.shadow){
16884             this.shadow.setZIndex(zindex + 1);
16885         }
16886         if(this.shim){
16887             this.shim.setStyle('z-index', zindex);
16888         }
16889         return this;
16890     }
16891 });
16892 })();
16893 /**
16894  * @class Ext.Shadow
16895  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
16896  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
16897  * functionality that can also provide the same shadow effect, see the {@link Ext.Layer} class.
16898  * @constructor
16899  * Create a new Shadow
16900  * @param {Object} config The config object
16901  */
16902 Ext.Shadow = function(config){
16903     Ext.apply(this, config);
16904     if(typeof this.mode != "string"){
16905         this.mode = this.defaultMode;
16906     }
16907     var o = this.offset, a = {h: 0};
16908     var rad = Math.floor(this.offset/2);
16909     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
16910         case "drop":
16911             a.w = 0;
16912             a.l = a.t = o;
16913             a.t -= 1;
16914             if(Ext.isIE){
16915                 a.l -= this.offset + rad;
16916                 a.t -= this.offset + rad;
16917                 a.w -= rad;
16918                 a.h -= rad;
16919                 a.t += 1;
16920             }
16921         break;
16922         case "sides":
16923             a.w = (o*2);
16924             a.l = -o;
16925             a.t = o-1;
16926             if(Ext.isIE){
16927                 a.l -= (this.offset - rad);
16928                 a.t -= this.offset + rad;
16929                 a.l += 1;
16930                 a.w -= (this.offset - rad)*2;
16931                 a.w -= rad + 1;
16932                 a.h -= 1;
16933             }
16934         break;
16935         case "frame":
16936             a.w = a.h = (o*2);
16937             a.l = a.t = -o;
16938             a.t += 1;
16939             a.h -= 2;
16940             if(Ext.isIE){
16941                 a.l -= (this.offset - rad);
16942                 a.t -= (this.offset - rad);
16943                 a.l += 1;
16944                 a.w -= (this.offset + rad + 1);
16945                 a.h -= (this.offset + rad);
16946                 a.h += 1;
16947             }
16948         break;
16949     };
16950
16951     this.adjusts = a;
16952 };
16953
16954 Ext.Shadow.prototype = {
16955     /**
16956      * @cfg {String} mode
16957      * The shadow display mode.  Supports the following options:<div class="mdetail-params"><ul>
16958      * <li><b><tt>sides</tt></b> : Shadow displays on both sides and bottom only</li>
16959      * <li><b><tt>frame</tt></b> : Shadow displays equally on all four sides</li>
16960      * <li><b><tt>drop</tt></b> : Traditional bottom-right drop shadow</li>
16961      * </ul></div>
16962      */
16963     /**
16964      * @cfg {String} offset
16965      * The number of pixels to offset the shadow from the element (defaults to <tt>4</tt>)
16966      */
16967     offset: 4,
16968
16969     // private
16970     defaultMode: "drop",
16971
16972     /**
16973      * Displays the shadow under the target element
16974      * @param {Mixed} targetEl The id or element under which the shadow should display
16975      */
16976     show : function(target){
16977         target = Ext.get(target);
16978         if(!this.el){
16979             this.el = Ext.Shadow.Pool.pull();
16980             if(this.el.dom.nextSibling != target.dom){
16981                 this.el.insertBefore(target);
16982             }
16983         }
16984         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
16985         if(Ext.isIE){
16986             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
16987         }
16988         this.realign(
16989             target.getLeft(true),
16990             target.getTop(true),
16991             target.getWidth(),
16992             target.getHeight()
16993         );
16994         this.el.dom.style.display = "block";
16995     },
16996
16997     /**
16998      * Returns true if the shadow is visible, else false
16999      */
17000     isVisible : function(){
17001         return this.el ? true : false;  
17002     },
17003
17004     /**
17005      * Direct alignment when values are already available. Show must be called at least once before
17006      * calling this method to ensure it is initialized.
17007      * @param {Number} left The target element left position
17008      * @param {Number} top The target element top position
17009      * @param {Number} width The target element width
17010      * @param {Number} height The target element height
17011      */
17012     realign : function(l, t, w, h){
17013         if(!this.el){
17014             return;
17015         }
17016         var a = this.adjusts, d = this.el.dom, s = d.style;
17017         var iea = 0;
17018         s.left = (l+a.l)+"px";
17019         s.top = (t+a.t)+"px";
17020         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
17021         if(s.width != sws || s.height != shs){
17022             s.width = sws;
17023             s.height = shs;
17024             if(!Ext.isIE){
17025                 var cn = d.childNodes;
17026                 var sww = Math.max(0, (sw-12))+"px";
17027                 cn[0].childNodes[1].style.width = sww;
17028                 cn[1].childNodes[1].style.width = sww;
17029                 cn[2].childNodes[1].style.width = sww;
17030                 cn[1].style.height = Math.max(0, (sh-12))+"px";
17031             }
17032         }
17033     },
17034
17035     /**
17036      * Hides this shadow
17037      */
17038     hide : function(){
17039         if(this.el){
17040             this.el.dom.style.display = "none";
17041             Ext.Shadow.Pool.push(this.el);
17042             delete this.el;
17043         }
17044     },
17045
17046     /**
17047      * Adjust the z-index of this shadow
17048      * @param {Number} zindex The new z-index
17049      */
17050     setZIndex : function(z){
17051         this.zIndex = z;
17052         if(this.el){
17053             this.el.setStyle("z-index", z);
17054         }
17055     }
17056 };
17057
17058 // Private utility class that manages the internal Shadow cache
17059 Ext.Shadow.Pool = function(){
17060     var p = [];
17061     var markup = Ext.isIE ?
17062                  '<div class="x-ie-shadow"></div>' :
17063                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
17064     return {
17065         pull : function(){
17066             var sh = p.shift();
17067             if(!sh){
17068                 sh = Ext.get(Ext.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
17069                 sh.autoBoxAdjust = false;
17070             }
17071             return sh;
17072         },
17073
17074         push : function(sh){
17075             p.push(sh);
17076         }
17077     };
17078 }();/**
17079  * @class Ext.BoxComponent
17080  * @extends Ext.Component
17081  * <p>Base class for any {@link Ext.Component Component} that is to be sized as a box, using width and height.</p>
17082  * <p>BoxComponent provides automatic box model adjustments for sizing and positioning and will work correctly
17083  * within the Component rendering model.</p>
17084  * <p>A BoxComponent may be created as a custom Component which encapsulates any HTML element, either a pre-existing
17085  * element, or one that is created to your specifications at render time. Usually, to participate in layouts,
17086  * a Component will need to be a <b>Box</b>Component in order to have its width and height managed.</p>
17087  * <p>To use a pre-existing element as a BoxComponent, configure it so that you preset the <b>el</b> property to the
17088  * element to reference:<pre><code>
17089 var pageHeader = new Ext.BoxComponent({
17090     el: 'my-header-div'
17091 });</code></pre>
17092  * This may then be {@link Ext.Container#add added} to a {@link Ext.Container Container} as a child item.</p>
17093  * <p>To create a BoxComponent based around a HTML element to be created at render time, use the
17094  * {@link Ext.Component#autoEl autoEl} config option which takes the form of a
17095  * {@link Ext.DomHelper DomHelper} specification:<pre><code>
17096 var myImage = new Ext.BoxComponent({
17097     autoEl: {
17098         tag: 'img',
17099         src: '/images/my-image.jpg'
17100     }
17101 });</code></pre></p>
17102  * @constructor
17103  * @param {Ext.Element/String/Object} config The configuration options.
17104  * @xtype box
17105  */
17106 Ext.BoxComponent = Ext.extend(Ext.Component, {
17107
17108     // Configs below are used for all Components when rendered by BoxLayout.
17109     /**
17110      * @cfg {Number} flex
17111      * <p><b>Note</b>: this config is only used when this Component is rendered
17112      * by a Container which has been configured to use a <b>{@link Ext.layout.BoxLayout BoxLayout}.</b>
17113      * Each child Component with a <code>flex</code> property will be flexed either vertically (by a VBoxLayout)
17114      * or horizontally (by an HBoxLayout) according to the item's <b>relative</b> <code>flex</code> value
17115      * compared to the sum of all Components with <code>flex</flex> value specified. Any child items that have
17116      * either a <code>flex = 0</code> or <code>flex = undefined</code> will not be 'flexed' (the initial size will not be changed).
17117      */
17118     // Configs below are used for all Components when rendered by AnchorLayout.
17119     /**
17120      * @cfg {String} anchor <p><b>Note</b>: this config is only used when this Component is rendered
17121      * by a Container which has been configured to use an <b>{@link Ext.layout.AnchorLayout AnchorLayout} (or subclass thereof).</b>
17122      * based layout manager, for example:<div class="mdetail-params"><ul>
17123      * <li>{@link Ext.form.FormPanel}</li>
17124      * <li>specifying <code>layout: 'anchor' // or 'form', or 'absolute'</code></li>
17125      * </ul></div></p>
17126      * <p>See {@link Ext.layout.AnchorLayout}.{@link Ext.layout.AnchorLayout#anchor anchor} also.</p>
17127      */
17128     // tabTip config is used when a BoxComponent is a child of a TabPanel
17129     /**
17130      * @cfg {String} tabTip
17131      * <p><b>Note</b>: this config is only used when this BoxComponent is a child item of a TabPanel.</p>
17132      * A string to be used as innerHTML (html tags are accepted) to show in a tooltip when mousing over
17133      * the associated tab selector element. {@link Ext.QuickTips}.init()
17134      * must be called in order for the tips to render.
17135      */
17136     // Configs below are used for all Components when rendered by BorderLayout.
17137     /**
17138      * @cfg {String} region <p><b>Note</b>: this config is only used when this BoxComponent is rendered
17139      * by a Container which has been configured to use the <b>{@link Ext.layout.BorderLayout BorderLayout}</b>
17140      * layout manager (e.g. specifying <tt>layout:'border'</tt>).</p><br>
17141      * <p>See {@link Ext.layout.BorderLayout} also.</p>
17142      */
17143     // margins config is used when a BoxComponent is rendered by BorderLayout or BoxLayout.
17144     /**
17145      * @cfg {Object} margins <p><b>Note</b>: this config is only used when this BoxComponent is rendered
17146      * by a Container which has been configured to use the <b>{@link Ext.layout.BorderLayout BorderLayout}</b>
17147      * or one of the two <b>{@link Ext.layout.BoxLayout BoxLayout} subclasses.</b></p>
17148      * <p>An object containing margins to apply to this BoxComponent in the
17149      * format:</p><pre><code>
17150 {
17151     top: (top margin),
17152     right: (right margin),
17153     bottom: (bottom margin),
17154     left: (left margin)
17155 }</code></pre>
17156      * <p>May also be a string containing space-separated, numeric margin values. The order of the
17157      * sides associated with each value matches the way CSS processes margin values:</p>
17158      * <p><div class="mdetail-params"><ul>
17159      * <li>If there is only one value, it applies to all sides.</li>
17160      * <li>If there are two values, the top and bottom borders are set to the first value and the
17161      * right and left are set to the second.</li>
17162      * <li>If there are three values, the top is set to the first value, the left and right are set
17163      * to the second, and the bottom is set to the third.</li>
17164      * <li>If there are four values, they apply to the top, right, bottom, and left, respectively.</li>
17165      * </ul></div></p>
17166      * <p>Defaults to:</p><pre><code>
17167      * {top:0, right:0, bottom:0, left:0}
17168      * </code></pre>
17169      */
17170     /**
17171      * @cfg {Number} x
17172      * The local x (left) coordinate for this component if contained within a positioning container.
17173      */
17174     /**
17175      * @cfg {Number} y
17176      * The local y (top) coordinate for this component if contained within a positioning container.
17177      */
17178     /**
17179      * @cfg {Number} pageX
17180      * The page level x coordinate for this component if contained within a positioning container.
17181      */
17182     /**
17183      * @cfg {Number} pageY
17184      * The page level y coordinate for this component if contained within a positioning container.
17185      */
17186     /**
17187      * @cfg {Number} height
17188      * The height of this component in pixels (defaults to auto).
17189      * <b>Note</b> to express this dimension as a percentage or offset see {@link Ext.Component#anchor}.
17190      */
17191     /**
17192      * @cfg {Number} width
17193      * The width of this component in pixels (defaults to auto).
17194      * <b>Note</b> to express this dimension as a percentage or offset see {@link Ext.Component#anchor}.
17195      */
17196     /**
17197      * @cfg {Number} boxMinHeight
17198      * <p>The minimum value in pixels which this BoxComponent will set its height to.</p>
17199      * <p><b>Warning:</b> This will override any size management applied by layout managers.</p>
17200      */
17201     /**
17202      * @cfg {Number} boxMinWidth
17203      * <p>The minimum value in pixels which this BoxComponent will set its width to.</p>
17204      * <p><b>Warning:</b> This will override any size management applied by layout managers.</p>
17205      */
17206     /**
17207      * @cfg {Number} boxMaxHeight
17208      * <p>The maximum value in pixels which this BoxComponent will set its height to.</p>
17209      * <p><b>Warning:</b> This will override any size management applied by layout managers.</p>
17210      */
17211     /**
17212      * @cfg {Number} boxMaxWidth
17213      * <p>The maximum value in pixels which this BoxComponent will set its width to.</p>
17214      * <p><b>Warning:</b> This will override any size management applied by layout managers.</p>
17215      */
17216     /**
17217      * @cfg {Boolean} autoHeight
17218      * <p>True to use height:'auto', false to use fixed height (or allow it to be managed by its parent
17219      * Container's {@link Ext.Container#layout layout manager}. Defaults to false.</p>
17220      * <p><b>Note</b>: Although many components inherit this config option, not all will
17221      * function as expected with a height of 'auto'. Setting autoHeight:true means that the
17222      * browser will manage height based on the element's contents, and that Ext will not manage it at all.</p>
17223      * <p>If the <i>browser</i> is managing the height, be aware that resizes performed by the browser in response
17224      * to changes within the structure of the Component cannot be detected. Therefore changes to the height might
17225      * result in elements needing to be synchronized with the new height. Example:</p><pre><code>
17226 var w = new Ext.Window({
17227     title: 'Window',
17228     width: 600,
17229     autoHeight: true,
17230     items: {
17231         title: 'Collapse Me',
17232         height: 400,
17233         collapsible: true,
17234         border: false,
17235         listeners: {
17236             beforecollapse: function() {
17237                 w.el.shadow.hide();
17238             },
17239             beforeexpand: function() {
17240                 w.el.shadow.hide();
17241             },
17242             collapse: function() {
17243                 w.syncShadow();
17244             },
17245             expand: function() {
17246                 w.syncShadow();
17247             }
17248         }
17249     }
17250 }).show();
17251 </code></pre>
17252      */
17253     /**
17254      * @cfg {Boolean} autoWidth
17255      * <p>True to use width:'auto', false to use fixed width (or allow it to be managed by its parent
17256      * Container's {@link Ext.Container#layout layout manager}. Defaults to false.</p>
17257      * <p><b>Note</b>: Although many components  inherit this config option, not all will
17258      * function as expected with a width of 'auto'. Setting autoWidth:true means that the
17259      * browser will manage width based on the element's contents, and that Ext will not manage it at all.</p>
17260      * <p>If the <i>browser</i> is managing the width, be aware that resizes performed by the browser in response
17261      * to changes within the structure of the Component cannot be detected. Therefore changes to the width might
17262      * result in elements needing to be synchronized with the new width. For example, where the target element is:</p><pre><code>
17263 &lt;div id='grid-container' style='margin-left:25%;width:50%'>&lt;/div>
17264 </code></pre>
17265      * A Panel rendered into that target element must listen for browser window resize in order to relay its
17266       * child items when the browser changes its width:<pre><code>
17267 var myPanel = new Ext.Panel({
17268     renderTo: 'grid-container',
17269     monitorResize: true, // relay on browser resize
17270     title: 'Panel',
17271     height: 400,
17272     autoWidth: true,
17273     layout: 'hbox',
17274     layoutConfig: {
17275         align: 'stretch'
17276     },
17277     defaults: {
17278         flex: 1
17279     },
17280     items: [{
17281         title: 'Box 1',
17282     }, {
17283         title: 'Box 2'
17284     }, {
17285         title: 'Box 3'
17286     }],
17287 });
17288 </code></pre>
17289      */
17290     /**
17291      * @cfg {Boolean} autoScroll
17292      * <code>true</code> to use overflow:'auto' on the components layout element and show scroll bars automatically when
17293      * necessary, <code>false</code> to clip any overflowing content (defaults to <code>false</code>).
17294      */
17295
17296     /* // private internal config
17297      * {Boolean} deferHeight
17298      * True to defer height calculations to an external component, false to allow this component to set its own
17299      * height (defaults to false).
17300      */
17301
17302     // private
17303     initComponent : function(){
17304         Ext.BoxComponent.superclass.initComponent.call(this);
17305         this.addEvents(
17306             /**
17307              * @event resize
17308              * Fires after the component is resized.
17309              * @param {Ext.Component} this
17310              * @param {Number} adjWidth The box-adjusted width that was set
17311              * @param {Number} adjHeight The box-adjusted height that was set
17312              * @param {Number} rawWidth The width that was originally specified
17313              * @param {Number} rawHeight The height that was originally specified
17314              */
17315             'resize',
17316             /**
17317              * @event move
17318              * Fires after the component is moved.
17319              * @param {Ext.Component} this
17320              * @param {Number} x The new x position
17321              * @param {Number} y The new y position
17322              */
17323             'move'
17324         );
17325     },
17326
17327     // private, set in afterRender to signify that the component has been rendered
17328     boxReady : false,
17329     // private, used to defer height settings to subclasses
17330     deferHeight: false,
17331
17332     /**
17333      * Sets the width and height of this BoxComponent. This method fires the {@link #resize} event. This method can accept
17334      * either width and height as separate arguments, or you can pass a size object like <code>{width:10, height:20}</code>.
17335      * @param {Mixed} width The new width to set. This may be one of:<div class="mdetail-params"><ul>
17336      * <li>A Number specifying the new width in the {@link #getEl Element}'s {@link Ext.Element#defaultUnit}s (by default, pixels).</li>
17337      * <li>A String used to set the CSS width style.</li>
17338      * <li>A size object in the format <code>{width: widthValue, height: heightValue}</code>.</li>
17339      * <li><code>undefined</code> to leave the width unchanged.</li>
17340      * </ul></div>
17341      * @param {Mixed} height The new height to set (not required if a size object is passed as the first arg).
17342      * This may be one of:<div class="mdetail-params"><ul>
17343      * <li>A Number specifying the new height in the {@link #getEl Element}'s {@link Ext.Element#defaultUnit}s (by default, pixels).</li>
17344      * <li>A String used to set the CSS height style. Animation may <b>not</b> be used.</li>
17345      * <li><code>undefined</code> to leave the height unchanged.</li>
17346      * </ul></div>
17347      * @return {Ext.BoxComponent} this
17348      */
17349     setSize : function(w, h){
17350
17351         // support for standard size objects
17352         if(typeof w == 'object'){
17353             h = w.height, w = w.width;
17354         }
17355         if (Ext.isDefined(w) && Ext.isDefined(this.boxMinWidth) && (w < this.boxMinWidth)) {
17356             w = this.boxMinWidth;
17357         }
17358         if (Ext.isDefined(h) && Ext.isDefined(this.boxMinHeight) && (h < this.boxMinHeight)) {
17359             h = this.boxMinHeight;
17360         }
17361         if (Ext.isDefined(w) && Ext.isDefined(this.boxMaxWidth) && (w > this.boxMaxWidth)) {
17362             w = this.boxMaxWidth;
17363         }
17364         if (Ext.isDefined(h) && Ext.isDefined(this.boxMaxHeight) && (h > this.boxMaxHeight)) {
17365             h = this.boxMaxHeight;
17366         }
17367         // not rendered
17368         if(!this.boxReady){
17369             this.width = w, this.height = h;
17370             return this;
17371         }
17372
17373         // prevent recalcs when not needed
17374         if(this.cacheSizes !== false && this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
17375             return this;
17376         }
17377         this.lastSize = {width: w, height: h};
17378         var adj = this.adjustSize(w, h),
17379             aw = adj.width,
17380             ah = adj.height,
17381             rz;
17382         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
17383             rz = this.getResizeEl();
17384             if(!this.deferHeight && aw !== undefined && ah !== undefined){
17385                 rz.setSize(aw, ah);
17386             }else if(!this.deferHeight && ah !== undefined){
17387                 rz.setHeight(ah);
17388             }else if(aw !== undefined){
17389                 rz.setWidth(aw);
17390             }
17391             this.onResize(aw, ah, w, h);
17392             this.fireEvent('resize', this, aw, ah, w, h);
17393         }
17394         return this;
17395     },
17396
17397     /**
17398      * Sets the width of the component.  This method fires the {@link #resize} event.
17399      * @param {Number} width The new width to setThis may be one of:<div class="mdetail-params"><ul>
17400      * <li>A Number specifying the new width in the {@link #getEl Element}'s {@link Ext.Element#defaultUnit}s (by default, pixels).</li>
17401      * <li>A String used to set the CSS width style.</li>
17402      * </ul></div>
17403      * @return {Ext.BoxComponent} this
17404      */
17405     setWidth : function(width){
17406         return this.setSize(width);
17407     },
17408
17409     /**
17410      * Sets the height of the component.  This method fires the {@link #resize} event.
17411      * @param {Number} height The new height to set. This may be one of:<div class="mdetail-params"><ul>
17412      * <li>A Number specifying the new height in the {@link #getEl Element}'s {@link Ext.Element#defaultUnit}s (by default, pixels).</li>
17413      * <li>A String used to set the CSS height style.</li>
17414      * <li><i>undefined</i> to leave the height unchanged.</li>
17415      * </ul></div>
17416      * @return {Ext.BoxComponent} this
17417      */
17418     setHeight : function(height){
17419         return this.setSize(undefined, height);
17420     },
17421
17422     /**
17423      * Gets the current size of the component's underlying element.
17424      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
17425      */
17426     getSize : function(){
17427         return this.getResizeEl().getSize();
17428     },
17429
17430     /**
17431      * Gets the current width of the component's underlying element.
17432      * @return {Number}
17433      */
17434     getWidth : function(){
17435         return this.getResizeEl().getWidth();
17436     },
17437
17438     /**
17439      * Gets the current height of the component's underlying element.
17440      * @return {Number}
17441      */
17442     getHeight : function(){
17443         return this.getResizeEl().getHeight();
17444     },
17445
17446     /**
17447      * Gets the current size of the component's underlying element, including space taken by its margins.
17448      * @return {Object} An object containing the element's size {width: (element width + left/right margins), height: (element height + top/bottom margins)}
17449      */
17450     getOuterSize : function(){
17451         var el = this.getResizeEl();
17452         return {width: el.getWidth() + el.getMargins('lr'),
17453                 height: el.getHeight() + el.getMargins('tb')};
17454     },
17455
17456     /**
17457      * Gets the current XY position of the component's underlying element.
17458      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
17459      * @return {Array} The XY position of the element (e.g., [100, 200])
17460      */
17461     getPosition : function(local){
17462         var el = this.getPositionEl();
17463         if(local === true){
17464             return [el.getLeft(true), el.getTop(true)];
17465         }
17466         return this.xy || el.getXY();
17467     },
17468
17469     /**
17470      * Gets the current box measurements of the component's underlying element.
17471      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
17472      * @return {Object} box An object in the format {x, y, width, height}
17473      */
17474     getBox : function(local){
17475         var pos = this.getPosition(local);
17476         var s = this.getSize();
17477         s.x = pos[0];
17478         s.y = pos[1];
17479         return s;
17480     },
17481
17482     /**
17483      * Sets the current box measurements of the component's underlying element.
17484      * @param {Object} box An object in the format {x, y, width, height}
17485      * @return {Ext.BoxComponent} this
17486      */
17487     updateBox : function(box){
17488         this.setSize(box.width, box.height);
17489         this.setPagePosition(box.x, box.y);
17490         return this;
17491     },
17492
17493     /**
17494      * <p>Returns the outermost Element of this Component which defines the Components overall size.</p>
17495      * <p><i>Usually</i> this will return the same Element as <code>{@link #getEl}</code>,
17496      * but in some cases, a Component may have some more wrapping Elements around its main
17497      * active Element.</p>
17498      * <p>An example is a ComboBox. It is encased in a <i>wrapping</i> Element which
17499      * contains both the <code>&lt;input></code> Element (which is what would be returned
17500      * by its <code>{@link #getEl}</code> method, <i>and</i> the trigger button Element.
17501      * This Element is returned as the <code>resizeEl</code>.
17502      * @return {Ext.Element} The Element which is to be resized by size managing layouts.
17503      */
17504     getResizeEl : function(){
17505         return this.resizeEl || this.el;
17506     },
17507
17508     /**
17509      * Sets the overflow on the content element of the component.
17510      * @param {Boolean} scroll True to allow the Component to auto scroll.
17511      * @return {Ext.BoxComponent} this
17512      */
17513     setAutoScroll : function(scroll){
17514         if(this.rendered){
17515             this.getContentTarget().setOverflow(scroll ? 'auto' : '');
17516         }
17517         this.autoScroll = scroll;
17518         return this;
17519     },
17520
17521     /**
17522      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
17523      * This method fires the {@link #move} event.
17524      * @param {Number} left The new left
17525      * @param {Number} top The new top
17526      * @return {Ext.BoxComponent} this
17527      */
17528     setPosition : function(x, y){
17529         if(x && typeof x[1] == 'number'){
17530             y = x[1];
17531             x = x[0];
17532         }
17533         this.x = x;
17534         this.y = y;
17535         if(!this.boxReady){
17536             return this;
17537         }
17538         var adj = this.adjustPosition(x, y);
17539         var ax = adj.x, ay = adj.y;
17540
17541         var el = this.getPositionEl();
17542         if(ax !== undefined || ay !== undefined){
17543             if(ax !== undefined && ay !== undefined){
17544                 el.setLeftTop(ax, ay);
17545             }else if(ax !== undefined){
17546                 el.setLeft(ax);
17547             }else if(ay !== undefined){
17548                 el.setTop(ay);
17549             }
17550             this.onPosition(ax, ay);
17551             this.fireEvent('move', this, ax, ay);
17552         }
17553         return this;
17554     },
17555
17556     /**
17557      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
17558      * This method fires the {@link #move} event.
17559      * @param {Number} x The new x position
17560      * @param {Number} y The new y position
17561      * @return {Ext.BoxComponent} this
17562      */
17563     setPagePosition : function(x, y){
17564         if(x && typeof x[1] == 'number'){
17565             y = x[1];
17566             x = x[0];
17567         }
17568         this.pageX = x;
17569         this.pageY = y;
17570         if(!this.boxReady){
17571             return;
17572         }
17573         if(x === undefined || y === undefined){ // cannot translate undefined points
17574             return;
17575         }
17576         var p = this.getPositionEl().translatePoints(x, y);
17577         this.setPosition(p.left, p.top);
17578         return this;
17579     },
17580
17581     // private
17582     afterRender : function(){
17583         Ext.BoxComponent.superclass.afterRender.call(this);
17584         if(this.resizeEl){
17585             this.resizeEl = Ext.get(this.resizeEl);
17586         }
17587         if(this.positionEl){
17588             this.positionEl = Ext.get(this.positionEl);
17589         }
17590         this.boxReady = true;
17591         Ext.isDefined(this.autoScroll) && this.setAutoScroll(this.autoScroll);
17592         this.setSize(this.width, this.height);
17593         if(this.x || this.y){
17594             this.setPosition(this.x, this.y);
17595         }else if(this.pageX || this.pageY){
17596             this.setPagePosition(this.pageX, this.pageY);
17597         }
17598     },
17599
17600     /**
17601      * Force the component's size to recalculate based on the underlying element's current height and width.
17602      * @return {Ext.BoxComponent} this
17603      */
17604     syncSize : function(){
17605         delete this.lastSize;
17606         this.setSize(this.autoWidth ? undefined : this.getResizeEl().getWidth(), this.autoHeight ? undefined : this.getResizeEl().getHeight());
17607         return this;
17608     },
17609
17610     /* // protected
17611      * Called after the component is resized, this method is empty by default but can be implemented by any
17612      * subclass that needs to perform custom logic after a resize occurs.
17613      * @param {Number} adjWidth The box-adjusted width that was set
17614      * @param {Number} adjHeight The box-adjusted height that was set
17615      * @param {Number} rawWidth The width that was originally specified
17616      * @param {Number} rawHeight The height that was originally specified
17617      */
17618     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
17619     },
17620
17621     /* // protected
17622      * Called after the component is moved, this method is empty by default but can be implemented by any
17623      * subclass that needs to perform custom logic after a move occurs.
17624      * @param {Number} x The new x position
17625      * @param {Number} y The new y position
17626      */
17627     onPosition : function(x, y){
17628
17629     },
17630
17631     // private
17632     adjustSize : function(w, h){
17633         if(this.autoWidth){
17634             w = 'auto';
17635         }
17636         if(this.autoHeight){
17637             h = 'auto';
17638         }
17639         return {width : w, height: h};
17640     },
17641
17642     // private
17643     adjustPosition : function(x, y){
17644         return {x : x, y: y};
17645     }
17646 });
17647 Ext.reg('box', Ext.BoxComponent);
17648
17649
17650 /**
17651  * @class Ext.Spacer
17652  * @extends Ext.BoxComponent
17653  * <p>Used to provide a sizable space in a layout.</p>
17654  * @constructor
17655  * @param {Object} config
17656  */
17657 Ext.Spacer = Ext.extend(Ext.BoxComponent, {
17658     autoEl:'div'
17659 });
17660 Ext.reg('spacer', Ext.Spacer);/**\r
17661  * @class Ext.SplitBar\r
17662  * @extends Ext.util.Observable\r
17663  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).\r
17664  * <br><br>\r
17665  * Usage:\r
17666  * <pre><code>\r
17667 var split = new Ext.SplitBar("elementToDrag", "elementToSize",\r
17668                    Ext.SplitBar.HORIZONTAL, Ext.SplitBar.LEFT);\r
17669 split.setAdapter(new Ext.SplitBar.AbsoluteLayoutAdapter("container"));\r
17670 split.minSize = 100;\r
17671 split.maxSize = 600;\r
17672 split.animate = true;\r
17673 split.on('moved', splitterMoved);\r
17674 </code></pre>\r
17675  * @constructor\r
17676  * Create a new SplitBar\r
17677  * @param {Mixed} dragElement The element to be dragged and act as the SplitBar.\r
17678  * @param {Mixed} resizingElement The element to be resized based on where the SplitBar element is dragged\r
17679  * @param {Number} orientation (optional) Either Ext.SplitBar.HORIZONTAL or Ext.SplitBar.VERTICAL. (Defaults to HORIZONTAL)\r
17680  * @param {Number} placement (optional) Either Ext.SplitBar.LEFT or Ext.SplitBar.RIGHT for horizontal or\r
17681                         Ext.SplitBar.TOP or Ext.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial\r
17682                         position of the SplitBar).\r
17683  */\r
17684 Ext.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){\r
17685 \r
17686     /** @private */\r
17687     this.el = Ext.get(dragElement, true);\r
17688     this.el.dom.unselectable = "on";\r
17689     /** @private */\r
17690     this.resizingEl = Ext.get(resizingElement, true);\r
17691 \r
17692     /**\r
17693      * @private\r
17694      * The orientation of the split. Either Ext.SplitBar.HORIZONTAL or Ext.SplitBar.VERTICAL. (Defaults to HORIZONTAL)\r
17695      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated\r
17696      * @type Number\r
17697      */\r
17698     this.orientation = orientation || Ext.SplitBar.HORIZONTAL;\r
17699 \r
17700     /**\r
17701      * The increment, in pixels by which to move this SplitBar. When <i>undefined</i>, the SplitBar moves smoothly.\r
17702      * @type Number\r
17703      * @property tickSize\r
17704      */\r
17705     /**\r
17706      * The minimum size of the resizing element. (Defaults to 0)\r
17707      * @type Number\r
17708      */\r
17709     this.minSize = 0;\r
17710 \r
17711     /**\r
17712      * The maximum size of the resizing element. (Defaults to 2000)\r
17713      * @type Number\r
17714      */\r
17715     this.maxSize = 2000;\r
17716 \r
17717     /**\r
17718      * Whether to animate the transition to the new size\r
17719      * @type Boolean\r
17720      */\r
17721     this.animate = false;\r
17722 \r
17723     /**\r
17724      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.\r
17725      * @type Boolean\r
17726      */\r
17727     this.useShim = false;\r
17728 \r
17729     /** @private */\r
17730     this.shim = null;\r
17731 \r
17732     if(!existingProxy){\r
17733         /** @private */\r
17734         this.proxy = Ext.SplitBar.createProxy(this.orientation);\r
17735     }else{\r
17736         this.proxy = Ext.get(existingProxy).dom;\r
17737     }\r
17738     /** @private */\r
17739     this.dd = new Ext.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});\r
17740 \r
17741     /** @private */\r
17742     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);\r
17743 \r
17744     /** @private */\r
17745     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);\r
17746 \r
17747     /** @private */\r
17748     this.dragSpecs = {};\r
17749 \r
17750     /**\r
17751      * @private The adapter to use to positon and resize elements\r
17752      */\r
17753     this.adapter = new Ext.SplitBar.BasicLayoutAdapter();\r
17754     this.adapter.init(this);\r
17755 \r
17756     if(this.orientation == Ext.SplitBar.HORIZONTAL){\r
17757         /** @private */\r
17758         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Ext.SplitBar.LEFT : Ext.SplitBar.RIGHT);\r
17759         this.el.addClass("x-splitbar-h");\r
17760     }else{\r
17761         /** @private */\r
17762         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Ext.SplitBar.TOP : Ext.SplitBar.BOTTOM);\r
17763         this.el.addClass("x-splitbar-v");\r
17764     }\r
17765 \r
17766     this.addEvents(\r
17767         /**\r
17768          * @event resize\r
17769          * Fires when the splitter is moved (alias for {@link #moved})\r
17770          * @param {Ext.SplitBar} this\r
17771          * @param {Number} newSize the new width or height\r
17772          */\r
17773         "resize",\r
17774         /**\r
17775          * @event moved\r
17776          * Fires when the splitter is moved\r
17777          * @param {Ext.SplitBar} this\r
17778          * @param {Number} newSize the new width or height\r
17779          */\r
17780         "moved",\r
17781         /**\r
17782          * @event beforeresize\r
17783          * Fires before the splitter is dragged\r
17784          * @param {Ext.SplitBar} this\r
17785          */\r
17786         "beforeresize",\r
17787 \r
17788         "beforeapply"\r
17789     );\r
17790 \r
17791     Ext.SplitBar.superclass.constructor.call(this);\r
17792 };\r
17793 \r
17794 Ext.extend(Ext.SplitBar, Ext.util.Observable, {\r
17795     onStartProxyDrag : function(x, y){\r
17796         this.fireEvent("beforeresize", this);\r
17797         this.overlay =  Ext.DomHelper.append(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);\r
17798         this.overlay.unselectable();\r
17799         this.overlay.setSize(Ext.lib.Dom.getViewWidth(true), Ext.lib.Dom.getViewHeight(true));\r
17800         this.overlay.show();\r
17801         Ext.get(this.proxy).setDisplayed("block");\r
17802         var size = this.adapter.getElementSize(this);\r
17803         this.activeMinSize = this.getMinimumSize();\r
17804         this.activeMaxSize = this.getMaximumSize();\r
17805         var c1 = size - this.activeMinSize;\r
17806         var c2 = Math.max(this.activeMaxSize - size, 0);\r
17807         if(this.orientation == Ext.SplitBar.HORIZONTAL){\r
17808             this.dd.resetConstraints();\r
17809             this.dd.setXConstraint(\r
17810                 this.placement == Ext.SplitBar.LEFT ? c1 : c2,\r
17811                 this.placement == Ext.SplitBar.LEFT ? c2 : c1,\r
17812                 this.tickSize\r
17813             );\r
17814             this.dd.setYConstraint(0, 0);\r
17815         }else{\r
17816             this.dd.resetConstraints();\r
17817             this.dd.setXConstraint(0, 0);\r
17818             this.dd.setYConstraint(\r
17819                 this.placement == Ext.SplitBar.TOP ? c1 : c2,\r
17820                 this.placement == Ext.SplitBar.TOP ? c2 : c1,\r
17821                 this.tickSize\r
17822             );\r
17823          }\r
17824         this.dragSpecs.startSize = size;\r
17825         this.dragSpecs.startPoint = [x, y];\r
17826         Ext.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);\r
17827     },\r
17828 \r
17829     /**\r
17830      * @private Called after the drag operation by the DDProxy\r
17831      */\r
17832     onEndProxyDrag : function(e){\r
17833         Ext.get(this.proxy).setDisplayed(false);\r
17834         var endPoint = Ext.lib.Event.getXY(e);\r
17835         if(this.overlay){\r
17836             Ext.destroy(this.overlay);\r
17837             delete this.overlay;\r
17838         }\r
17839         var newSize;\r
17840         if(this.orientation == Ext.SplitBar.HORIZONTAL){\r
17841             newSize = this.dragSpecs.startSize +\r
17842                 (this.placement == Ext.SplitBar.LEFT ?\r
17843                     endPoint[0] - this.dragSpecs.startPoint[0] :\r
17844                     this.dragSpecs.startPoint[0] - endPoint[0]\r
17845                 );\r
17846         }else{\r
17847             newSize = this.dragSpecs.startSize +\r
17848                 (this.placement == Ext.SplitBar.TOP ?\r
17849                     endPoint[1] - this.dragSpecs.startPoint[1] :\r
17850                     this.dragSpecs.startPoint[1] - endPoint[1]\r
17851                 );\r
17852         }\r
17853         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);\r
17854         if(newSize != this.dragSpecs.startSize){\r
17855             if(this.fireEvent('beforeapply', this, newSize) !== false){\r
17856                 this.adapter.setElementSize(this, newSize);\r
17857                 this.fireEvent("moved", this, newSize);\r
17858                 this.fireEvent("resize", this, newSize);\r
17859             }\r
17860         }\r
17861     },\r
17862 \r
17863     /**\r
17864      * Get the adapter this SplitBar uses\r
17865      * @return The adapter object\r
17866      */\r
17867     getAdapter : function(){\r
17868         return this.adapter;\r
17869     },\r
17870 \r
17871     /**\r
17872      * Set the adapter this SplitBar uses\r
17873      * @param {Object} adapter A SplitBar adapter object\r
17874      */\r
17875     setAdapter : function(adapter){\r
17876         this.adapter = adapter;\r
17877         this.adapter.init(this);\r
17878     },\r
17879 \r
17880     /**\r
17881      * Gets the minimum size for the resizing element\r
17882      * @return {Number} The minimum size\r
17883      */\r
17884     getMinimumSize : function(){\r
17885         return this.minSize;\r
17886     },\r
17887 \r
17888     /**\r
17889      * Sets the minimum size for the resizing element\r
17890      * @param {Number} minSize The minimum size\r
17891      */\r
17892     setMinimumSize : function(minSize){\r
17893         this.minSize = minSize;\r
17894     },\r
17895 \r
17896     /**\r
17897      * Gets the maximum size for the resizing element\r
17898      * @return {Number} The maximum size\r
17899      */\r
17900     getMaximumSize : function(){\r
17901         return this.maxSize;\r
17902     },\r
17903 \r
17904     /**\r
17905      * Sets the maximum size for the resizing element\r
17906      * @param {Number} maxSize The maximum size\r
17907      */\r
17908     setMaximumSize : function(maxSize){\r
17909         this.maxSize = maxSize;\r
17910     },\r
17911 \r
17912     /**\r
17913      * Sets the initialize size for the resizing element\r
17914      * @param {Number} size The initial size\r
17915      */\r
17916     setCurrentSize : function(size){\r
17917         var oldAnimate = this.animate;\r
17918         this.animate = false;\r
17919         this.adapter.setElementSize(this, size);\r
17920         this.animate = oldAnimate;\r
17921     },\r
17922 \r
17923     /**\r
17924      * Destroy this splitbar.\r
17925      * @param {Boolean} removeEl True to remove the element\r
17926      */\r
17927     destroy : function(removeEl){\r
17928         Ext.destroy(this.shim, Ext.get(this.proxy));\r
17929         this.dd.unreg();\r
17930         if(removeEl){\r
17931             this.el.remove();\r
17932         }\r
17933         this.purgeListeners();\r
17934     }\r
17935 });\r
17936 \r
17937 /**\r
17938  * @private static Create our own proxy element element. So it will be the same same size on all browsers, we won't use borders. Instead we use a background color.\r
17939  */\r
17940 Ext.SplitBar.createProxy = function(dir){\r
17941     var proxy = new Ext.Element(document.createElement("div"));\r
17942     document.body.appendChild(proxy.dom);\r
17943     proxy.unselectable();\r
17944     var cls = 'x-splitbar-proxy';\r
17945     proxy.addClass(cls + ' ' + (dir == Ext.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));\r
17946     return proxy.dom;\r
17947 };\r
17948 \r
17949 /**\r
17950  * @class Ext.SplitBar.BasicLayoutAdapter\r
17951  * Default Adapter. It assumes the splitter and resizing element are not positioned\r
17952  * elements and only gets/sets the width of the element. Generally used for table based layouts.\r
17953  */\r
17954 Ext.SplitBar.BasicLayoutAdapter = function(){\r
17955 };\r
17956 \r
17957 Ext.SplitBar.BasicLayoutAdapter.prototype = {\r
17958     // do nothing for now\r
17959     init : function(s){\r
17960 \r
17961     },\r
17962     /**\r
17963      * Called before drag operations to get the current size of the resizing element.\r
17964      * @param {Ext.SplitBar} s The SplitBar using this adapter\r
17965      */\r
17966      getElementSize : function(s){\r
17967         if(s.orientation == Ext.SplitBar.HORIZONTAL){\r
17968             return s.resizingEl.getWidth();\r
17969         }else{\r
17970             return s.resizingEl.getHeight();\r
17971         }\r
17972     },\r
17973 \r
17974     /**\r
17975      * Called after drag operations to set the size of the resizing element.\r
17976      * @param {Ext.SplitBar} s The SplitBar using this adapter\r
17977      * @param {Number} newSize The new size to set\r
17978      * @param {Function} onComplete A function to be invoked when resizing is complete\r
17979      */\r
17980     setElementSize : function(s, newSize, onComplete){\r
17981         if(s.orientation == Ext.SplitBar.HORIZONTAL){\r
17982             if(!s.animate){\r
17983                 s.resizingEl.setWidth(newSize);\r
17984                 if(onComplete){\r
17985                     onComplete(s, newSize);\r
17986                 }\r
17987             }else{\r
17988                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');\r
17989             }\r
17990         }else{\r
17991 \r
17992             if(!s.animate){\r
17993                 s.resizingEl.setHeight(newSize);\r
17994                 if(onComplete){\r
17995                     onComplete(s, newSize);\r
17996                 }\r
17997             }else{\r
17998                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');\r
17999             }\r
18000         }\r
18001     }\r
18002 };\r
18003 \r
18004 /**\r
18005  *@class Ext.SplitBar.AbsoluteLayoutAdapter\r
18006  * @extends Ext.SplitBar.BasicLayoutAdapter\r
18007  * Adapter that  moves the splitter element to align with the resized sizing element.\r
18008  * Used with an absolute positioned SplitBar.\r
18009  * @param {Mixed} container The container that wraps around the absolute positioned content. If it's\r
18010  * document.body, make sure you assign an id to the body element.\r
18011  */\r
18012 Ext.SplitBar.AbsoluteLayoutAdapter = function(container){\r
18013     this.basic = new Ext.SplitBar.BasicLayoutAdapter();\r
18014     this.container = Ext.get(container);\r
18015 };\r
18016 \r
18017 Ext.SplitBar.AbsoluteLayoutAdapter.prototype = {\r
18018     init : function(s){\r
18019         this.basic.init(s);\r
18020     },\r
18021 \r
18022     getElementSize : function(s){\r
18023         return this.basic.getElementSize(s);\r
18024     },\r
18025 \r
18026     setElementSize : function(s, newSize, onComplete){\r
18027         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));\r
18028     },\r
18029 \r
18030     moveSplitter : function(s){\r
18031         var yes = Ext.SplitBar;\r
18032         switch(s.placement){\r
18033             case yes.LEFT:\r
18034                 s.el.setX(s.resizingEl.getRight());\r
18035                 break;\r
18036             case yes.RIGHT:\r
18037                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");\r
18038                 break;\r
18039             case yes.TOP:\r
18040                 s.el.setY(s.resizingEl.getBottom());\r
18041                 break;\r
18042             case yes.BOTTOM:\r
18043                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());\r
18044                 break;\r
18045         }\r
18046     }\r
18047 };\r
18048 \r
18049 /**\r
18050  * Orientation constant - Create a vertical SplitBar\r
18051  * @static\r
18052  * @type Number\r
18053  */\r
18054 Ext.SplitBar.VERTICAL = 1;\r
18055 \r
18056 /**\r
18057  * Orientation constant - Create a horizontal SplitBar\r
18058  * @static\r
18059  * @type Number\r
18060  */\r
18061 Ext.SplitBar.HORIZONTAL = 2;\r
18062 \r
18063 /**\r
18064  * Placement constant - The resizing element is to the left of the splitter element\r
18065  * @static\r
18066  * @type Number\r
18067  */\r
18068 Ext.SplitBar.LEFT = 1;\r
18069 \r
18070 /**\r
18071  * Placement constant - The resizing element is to the right of the splitter element\r
18072  * @static\r
18073  * @type Number\r
18074  */\r
18075 Ext.SplitBar.RIGHT = 2;\r
18076 \r
18077 /**\r
18078  * Placement constant - The resizing element is positioned above the splitter element\r
18079  * @static\r
18080  * @type Number\r
18081  */\r
18082 Ext.SplitBar.TOP = 3;\r
18083 \r
18084 /**\r
18085  * Placement constant - The resizing element is positioned under splitter element\r
18086  * @static\r
18087  * @type Number\r
18088  */\r
18089 Ext.SplitBar.BOTTOM = 4;\r
18090 /**
18091  * @class Ext.Container
18092  * @extends Ext.BoxComponent
18093  * <p>Base class for any {@link Ext.BoxComponent} that may contain other Components. Containers handle the
18094  * basic behavior of containing items, namely adding, inserting and removing items.</p>
18095  *
18096  * <p>The most commonly used Container classes are {@link Ext.Panel}, {@link Ext.Window} and {@link Ext.TabPanel}.
18097  * If you do not need the capabilities offered by the aforementioned classes you can create a lightweight
18098  * Container to be encapsulated by an HTML element to your specifications by using the
18099  * <code><b>{@link Ext.Component#autoEl autoEl}</b></code> config option. This is a useful technique when creating
18100  * embedded {@link Ext.layout.ColumnLayout column} layouts inside {@link Ext.form.FormPanel FormPanels}
18101  * for example.</p>
18102  *
18103  * <p>The code below illustrates both how to explicitly create a Container, and how to implicitly
18104  * create one using the <b><code>'container'</code></b> xtype:<pre><code>
18105 // explicitly create a Container
18106 var embeddedColumns = new Ext.Container({
18107     autoEl: 'div',  // This is the default
18108     layout: 'column',
18109     defaults: {
18110         // implicitly create Container by specifying xtype
18111         xtype: 'container',
18112         autoEl: 'div', // This is the default.
18113         layout: 'form',
18114         columnWidth: 0.5,
18115         style: {
18116             padding: '10px'
18117         }
18118     },
18119 //  The two items below will be Ext.Containers, each encapsulated by a &lt;DIV> element.
18120     items: [{
18121         items: {
18122             xtype: 'datefield',
18123             name: 'startDate',
18124             fieldLabel: 'Start date'
18125         }
18126     }, {
18127         items: {
18128             xtype: 'datefield',
18129             name: 'endDate',
18130             fieldLabel: 'End date'
18131         }
18132     }]
18133 });</code></pre></p>
18134  *
18135  * <p><u><b>Layout</b></u></p>
18136  * <p>Container classes delegate the rendering of child Components to a layout
18137  * manager class which must be configured into the Container using the
18138  * <code><b>{@link #layout}</b></code> configuration property.</p>
18139  * <p>When either specifying child <code>{@link #items}</code> of a Container,
18140  * or dynamically {@link #add adding} Components to a Container, remember to
18141  * consider how you wish the Container to arrange those child elements, and
18142  * whether those child elements need to be sized using one of Ext's built-in
18143  * <b><code>{@link #layout}</code></b> schemes. By default, Containers use the
18144  * {@link Ext.layout.ContainerLayout ContainerLayout} scheme which only
18145  * renders child components, appending them one after the other inside the
18146  * Container, and <b>does not apply any sizing</b> at all.</p>
18147  * <p>A common mistake is when a developer neglects to specify a
18148  * <b><code>{@link #layout}</code></b> (e.g. widgets like GridPanels or
18149  * TreePanels are added to Containers for which no <code><b>{@link #layout}</b></code>
18150  * has been specified). If a Container is left to use the default
18151  * {@link Ext.layout.ContainerLayout ContainerLayout} scheme, none of its
18152  * child components will be resized, or changed in any way when the Container
18153  * is resized.</p>
18154  * <p>Certain layout managers allow dynamic addition of child components.
18155  * Those that do include {@link Ext.layout.CardLayout},
18156  * {@link Ext.layout.AnchorLayout}, {@link Ext.layout.FormLayout}, and
18157  * {@link Ext.layout.TableLayout}. For example:<pre><code>
18158 //  Create the GridPanel.
18159 var myNewGrid = new Ext.grid.GridPanel({
18160     store: myStore,
18161     columns: myColumnModel,
18162     title: 'Results', // the title becomes the title of the tab
18163 });
18164
18165 myTabPanel.add(myNewGrid); // {@link Ext.TabPanel} implicitly uses {@link Ext.layout.CardLayout CardLayout}
18166 myTabPanel.{@link Ext.TabPanel#setActiveTab setActiveTab}(myNewGrid);
18167  * </code></pre></p>
18168  * <p>The example above adds a newly created GridPanel to a TabPanel. Note that
18169  * a TabPanel uses {@link Ext.layout.CardLayout} as its layout manager which
18170  * means all its child items are sized to {@link Ext.layout.FitLayout fit}
18171  * exactly into its client area.
18172  * <p><b><u>Overnesting is a common problem</u></b>.
18173  * An example of overnesting occurs when a GridPanel is added to a TabPanel
18174  * by wrapping the GridPanel <i>inside</i> a wrapping Panel (that has no
18175  * <code><b>{@link #layout}</b></code> specified) and then add that wrapping Panel
18176  * to the TabPanel. The point to realize is that a GridPanel <b>is</b> a
18177  * Component which can be added directly to a Container. If the wrapping Panel
18178  * has no <code><b>{@link #layout}</b></code> configuration, then the overnested
18179  * GridPanel will not be sized as expected.<p>
18180  *
18181  * <p><u><b>Adding via remote configuration</b></u></p>
18182  *
18183  * <p>A server side script can be used to add Components which are generated dynamically on the server.
18184  * An example of adding a GridPanel to a TabPanel where the GridPanel is generated by the server
18185  * based on certain parameters:
18186  * </p><pre><code>
18187 // execute an Ajax request to invoke server side script:
18188 Ext.Ajax.request({
18189     url: 'gen-invoice-grid.php',
18190     // send additional parameters to instruct server script
18191     params: {
18192         startDate: Ext.getCmp('start-date').getValue(),
18193         endDate: Ext.getCmp('end-date').getValue()
18194     },
18195     // process the response object to add it to the TabPanel:
18196     success: function(xhr) {
18197         var newComponent = eval(xhr.responseText); // see discussion below
18198         myTabPanel.add(newComponent); // add the component to the TabPanel
18199         myTabPanel.setActiveTab(newComponent);
18200     },
18201     failure: function() {
18202         Ext.Msg.alert("Grid create failed", "Server communication failure");
18203     }
18204 });
18205 </code></pre>
18206  * <p>The server script needs to return an executable Javascript statement which, when processed
18207  * using <code>eval()</code>, will return either a config object with an {@link Ext.Component#xtype xtype},
18208  * or an instantiated Component. The server might return this for example:</p><pre><code>
18209 (function() {
18210     function formatDate(value){
18211         return value ? value.dateFormat('M d, Y') : '';
18212     };
18213
18214     var store = new Ext.data.Store({
18215         url: 'get-invoice-data.php',
18216         baseParams: {
18217             startDate: '01/01/2008',
18218             endDate: '01/31/2008'
18219         },
18220         reader: new Ext.data.JsonReader({
18221             record: 'transaction',
18222             idProperty: 'id',
18223             totalRecords: 'total'
18224         }, [
18225            'customer',
18226            'invNo',
18227            {name: 'date', type: 'date', dateFormat: 'm/d/Y'},
18228            {name: 'value', type: 'float'}
18229         ])
18230     });
18231
18232     var grid = new Ext.grid.GridPanel({
18233         title: 'Invoice Report',
18234         bbar: new Ext.PagingToolbar(store),
18235         store: store,
18236         columns: [
18237             {header: "Customer", width: 250, dataIndex: 'customer', sortable: true},
18238             {header: "Invoice Number", width: 120, dataIndex: 'invNo', sortable: true},
18239             {header: "Invoice Date", width: 100, dataIndex: 'date', renderer: formatDate, sortable: true},
18240             {header: "Value", width: 120, dataIndex: 'value', renderer: 'usMoney', sortable: true}
18241         ],
18242     });
18243     store.load();
18244     return grid;  // return instantiated component
18245 })();
18246 </code></pre>
18247  * <p>When the above code fragment is passed through the <code>eval</code> function in the success handler
18248  * of the Ajax request, the code is executed by the Javascript processor, and the anonymous function
18249  * runs, and returns the instantiated grid component.</p>
18250  * <p>Note: since the code above is <i>generated</i> by a server script, the <code>baseParams</code> for
18251  * the Store, the metadata to allow generation of the Record layout, and the ColumnModel
18252  * can all be generated into the code since these are all known on the server.</p>
18253  *
18254  * @xtype container
18255  */
18256 Ext.Container = Ext.extend(Ext.BoxComponent, {
18257     /**
18258      * @cfg {Boolean} monitorResize
18259      * True to automatically monitor window resize events to handle anything that is sensitive to the current size
18260      * of the viewport.  This value is typically managed by the chosen <code>{@link #layout}</code> and should not need
18261      * to be set manually.
18262      */
18263     /**
18264      * @cfg {String/Object} layout
18265      * <p><b>*Important</b>: In order for child items to be correctly sized and
18266      * positioned, typically a layout manager <b>must</b> be specified through
18267      * the <code>layout</code> configuration option.</p>
18268      * <br><p>The sizing and positioning of child {@link items} is the responsibility of
18269      * the Container's layout manager which creates and manages the type of layout
18270      * you have in mind.  For example:</p><pre><code>
18271 new Ext.Window({
18272     width:300, height: 300,
18273     layout: 'fit', // explicitly set layout manager: override the default (layout:'auto')
18274     items: [{
18275         title: 'Panel inside a Window'
18276     }]
18277 }).show();
18278      * </code></pre>
18279      * <p>If the {@link #layout} configuration is not explicitly specified for
18280      * a general purpose container (e.g. Container or Panel) the
18281      * {@link Ext.layout.ContainerLayout default layout manager} will be used
18282      * which does nothing but render child components sequentially into the
18283      * Container (no sizing or positioning will be performed in this situation).
18284      * Some container classes implicitly specify a default layout
18285      * (e.g. FormPanel specifies <code>layout:'form'</code>). Other specific
18286      * purpose classes internally specify/manage their internal layout (e.g.
18287      * GridPanel, TabPanel, TreePanel, Toolbar, Menu, etc.).</p>
18288      * <br><p><b><code>layout</code></b> may be specified as either as an Object or
18289      * as a String:</p><div><ul class="mdetail-params">
18290      *
18291      * <li><u>Specify as an Object</u></li>
18292      * <div><ul class="mdetail-params">
18293      * <li>Example usage:</li>
18294 <pre><code>
18295 layout: {
18296     type: 'vbox',
18297     padding: '5',
18298     align: 'left'
18299 }
18300 </code></pre>
18301      *
18302      * <li><code><b>type</b></code></li>
18303      * <br/><p>The layout type to be used for this container.  If not specified,
18304      * a default {@link Ext.layout.ContainerLayout} will be created and used.</p>
18305      * <br/><p>Valid layout <code>type</code> values are:</p>
18306      * <div class="sub-desc"><ul class="mdetail-params">
18307      * <li><code><b>{@link Ext.layout.AbsoluteLayout absolute}</b></code></li>
18308      * <li><code><b>{@link Ext.layout.AccordionLayout accordion}</b></code></li>
18309      * <li><code><b>{@link Ext.layout.AnchorLayout anchor}</b></code></li>
18310      * <li><code><b>{@link Ext.layout.ContainerLayout auto}</b></code> &nbsp;&nbsp;&nbsp; <b>Default</b></li>
18311      * <li><code><b>{@link Ext.layout.BorderLayout border}</b></code></li>
18312      * <li><code><b>{@link Ext.layout.CardLayout card}</b></code></li>
18313      * <li><code><b>{@link Ext.layout.ColumnLayout column}</b></code></li>
18314      * <li><code><b>{@link Ext.layout.FitLayout fit}</b></code></li>
18315      * <li><code><b>{@link Ext.layout.FormLayout form}</b></code></li>
18316      * <li><code><b>{@link Ext.layout.HBoxLayout hbox}</b></code></li>
18317      * <li><code><b>{@link Ext.layout.MenuLayout menu}</b></code></li>
18318      * <li><code><b>{@link Ext.layout.TableLayout table}</b></code></li>
18319      * <li><code><b>{@link Ext.layout.ToolbarLayout toolbar}</b></code></li>
18320      * <li><code><b>{@link Ext.layout.VBoxLayout vbox}</b></code></li>
18321      * </ul></div>
18322      *
18323      * <li>Layout specific configuration properties</li>
18324      * <br/><p>Additional layout specific configuration properties may also be
18325      * specified. For complete details regarding the valid config options for
18326      * each layout type, see the layout class corresponding to the <code>type</code>
18327      * specified.</p>
18328      *
18329      * </ul></div>
18330      *
18331      * <li><u>Specify as a String</u></li>
18332      * <div><ul class="mdetail-params">
18333      * <li>Example usage:</li>
18334 <pre><code>
18335 layout: 'vbox',
18336 layoutConfig: {
18337     padding: '5',
18338     align: 'left'
18339 }
18340 </code></pre>
18341      * <li><code><b>layout</b></code></li>
18342      * <br/><p>The layout <code>type</code> to be used for this container (see list
18343      * of valid layout type values above).</p><br/>
18344      * <li><code><b>{@link #layoutConfig}</b></code></li>
18345      * <br/><p>Additional layout specific configuration properties. For complete
18346      * details regarding the valid config options for each layout type, see the
18347      * layout class corresponding to the <code>layout</code> specified.</p>
18348      * </ul></div></ul></div>
18349      */
18350     /**
18351      * @cfg {Object} layoutConfig
18352      * This is a config object containing properties specific to the chosen
18353      * <b><code>{@link #layout}</code></b> if <b><code>{@link #layout}</code></b>
18354      * has been specified as a <i>string</i>.</p>
18355      */
18356     /**
18357      * @cfg {Boolean/Number} bufferResize
18358      * When set to true (50 milliseconds) or a number of milliseconds, the layout assigned for this container will buffer
18359      * the frequency it calculates and does a re-layout of components. This is useful for heavy containers or containers
18360      * with a large quantity of sub-components for which frequent layout calls would be expensive. Defaults to <code>50</code>.
18361      */
18362     // Deprecated - will be removed in 3.2.x
18363     bufferResize: 50,
18364
18365     /**
18366      * @cfg {String/Number} activeItem
18367      * A string component id or the numeric index of the component that should be initially activated within the
18368      * container's layout on render.  For example, activeItem: 'item-1' or activeItem: 0 (index 0 = the first
18369      * item in the container's collection).  activeItem only applies to layout styles that can display
18370      * items one at a time (like {@link Ext.layout.AccordionLayout}, {@link Ext.layout.CardLayout} and
18371      * {@link Ext.layout.FitLayout}).  Related to {@link Ext.layout.ContainerLayout#activeItem}.
18372      */
18373     /**
18374      * @cfg {Object/Array} items
18375      * <pre><b>** IMPORTANT</b>: be sure to <b>{@link #layout specify a <code>layout</code>} if needed ! **</b></pre>
18376      * <p>A single item, or an array of child Components to be added to this container,
18377      * for example:</p>
18378      * <pre><code>
18379 // specifying a single item
18380 items: {...},
18381 layout: 'fit',    // specify a layout!
18382
18383 // specifying multiple items
18384 items: [{...}, {...}],
18385 layout: 'anchor', // specify a layout!
18386      * </code></pre>
18387      * <p>Each item may be:</p>
18388      * <div><ul class="mdetail-params">
18389      * <li>any type of object based on {@link Ext.Component}</li>
18390      * <li>a fully instanciated object or</li>
18391      * <li>an object literal that:</li>
18392      * <div><ul class="mdetail-params">
18393      * <li>has a specified <code>{@link Ext.Component#xtype xtype}</code></li>
18394      * <li>the {@link Ext.Component#xtype} specified is associated with the Component
18395      * desired and should be chosen from one of the available xtypes as listed
18396      * in {@link Ext.Component}.</li>
18397      * <li>If an <code>{@link Ext.Component#xtype xtype}</code> is not explicitly
18398      * specified, the {@link #defaultType} for that Container is used.</li>
18399      * <li>will be "lazily instanciated", avoiding the overhead of constructing a fully
18400      * instanciated Component object</li>
18401      * </ul></div></ul></div>
18402      * <p><b>Notes</b>:</p>
18403      * <div><ul class="mdetail-params">
18404      * <li>Ext uses lazy rendering. Child Components will only be rendered
18405      * should it become necessary. Items are automatically laid out when they are first
18406      * shown (no sizing is done while hidden), or in response to a {@link #doLayout} call.</li>
18407      * <li>Do not specify <code>{@link Ext.Panel#contentEl contentEl}</code>/
18408      * <code>{@link Ext.Panel#html html}</code> with <code>items</code>.</li>
18409      * </ul></div>
18410      */
18411     /**
18412      * @cfg {Object|Function} defaults
18413      * <p>This option is a means of applying default settings to all added items whether added through the {@link #items}
18414      * config or via the {@link #add} or {@link #insert} methods.</p>
18415      * <p>If an added item is a config object, and <b>not</b> an instantiated Component, then the default properties are
18416      * unconditionally applied. If the added item <b>is</b> an instantiated Component, then the default properties are
18417      * applied conditionally so as not to override existing properties in the item.</p>
18418      * <p>If the defaults option is specified as a function, then the function will be called using this Container as the
18419      * scope (<code>this</code> reference) and passing the added item as the first parameter. Any resulting object
18420      * from that call is then applied to the item as default properties.</p>
18421      * <p>For example, to automatically apply padding to the body of each of a set of
18422      * contained {@link Ext.Panel} items, you could pass: <code>defaults: {bodyStyle:'padding:15px'}</code>.</p>
18423      * <p>Usage:</p><pre><code>
18424 defaults: {               // defaults are applied to items, not the container
18425     autoScroll:true
18426 },
18427 items: [
18428     {
18429         xtype: 'panel',   // defaults <b>do not</b> have precedence over
18430         id: 'panel1',     // options in config objects, so the defaults
18431         autoScroll: false // will not be applied here, panel1 will be autoScroll:false
18432     },
18433     new Ext.Panel({       // defaults <b>do</b> have precedence over options
18434         id: 'panel2',     // options in components, so the defaults
18435         autoScroll: false // will be applied here, panel2 will be autoScroll:true.
18436     })
18437 ]
18438      * </code></pre>
18439      */
18440
18441
18442     /** @cfg {Boolean} autoDestroy
18443      * If true the container will automatically destroy any contained component that is removed from it, else
18444      * destruction must be handled manually (defaults to true).
18445      */
18446     autoDestroy : true,
18447
18448     /** @cfg {Boolean} forceLayout
18449      * If true the container will force a layout initially even if hidden or collapsed. This option
18450      * is useful for forcing forms to render in collapsed or hidden containers. (defaults to false).
18451      */
18452     forceLayout: false,
18453
18454     /** @cfg {Boolean} hideBorders
18455      * True to hide the borders of each contained component, false to defer to the component's existing
18456      * border settings (defaults to false).
18457      */
18458     /** @cfg {String} defaultType
18459      * <p>The default {@link Ext.Component xtype} of child Components to create in this Container when
18460      * a child item is specified as a raw configuration object, rather than as an instantiated Component.</p>
18461      * <p>Defaults to <code>'panel'</code>, except {@link Ext.menu.Menu} which defaults to <code>'menuitem'</code>,
18462      * and {@link Ext.Toolbar} and {@link Ext.ButtonGroup} which default to <code>'button'</code>.</p>
18463      */
18464     defaultType : 'panel',
18465
18466     /** @cfg {String} resizeEvent
18467      * The event to listen to for resizing in layouts. Defaults to <code>'resize'</code>.
18468      */
18469     resizeEvent: 'resize',
18470
18471     /**
18472      * @cfg {Array} bubbleEvents
18473      * <p>An array of events that, when fired, should be bubbled to any parent container.
18474      * See {@link Ext.util.Observable#enableBubble}.
18475      * Defaults to <code>['add', 'remove']</code>.
18476      */
18477     bubbleEvents: ['add', 'remove'],
18478
18479     // private
18480     initComponent : function(){
18481         Ext.Container.superclass.initComponent.call(this);
18482
18483         this.addEvents(
18484             /**
18485              * @event afterlayout
18486              * Fires when the components in this container are arranged by the associated layout manager.
18487              * @param {Ext.Container} this
18488              * @param {ContainerLayout} layout The ContainerLayout implementation for this container
18489              */
18490             'afterlayout',
18491             /**
18492              * @event beforeadd
18493              * Fires before any {@link Ext.Component} is added or inserted into the container.
18494              * A handler can return false to cancel the add.
18495              * @param {Ext.Container} this
18496              * @param {Ext.Component} component The component being added
18497              * @param {Number} index The index at which the component will be added to the container's items collection
18498              */
18499             'beforeadd',
18500             /**
18501              * @event beforeremove
18502              * Fires before any {@link Ext.Component} is removed from the container.  A handler can return
18503              * false to cancel the remove.
18504              * @param {Ext.Container} this
18505              * @param {Ext.Component} component The component being removed
18506              */
18507             'beforeremove',
18508             /**
18509              * @event add
18510              * @bubbles
18511              * Fires after any {@link Ext.Component} is added or inserted into the container.
18512              * @param {Ext.Container} this
18513              * @param {Ext.Component} component The component that was added
18514              * @param {Number} index The index at which the component was added to the container's items collection
18515              */
18516             'add',
18517             /**
18518              * @event remove
18519              * @bubbles
18520              * Fires after any {@link Ext.Component} is removed from the container.
18521              * @param {Ext.Container} this
18522              * @param {Ext.Component} component The component that was removed
18523              */
18524             'remove'
18525         );
18526
18527         this.enableBubble(this.bubbleEvents);
18528
18529         /**
18530          * The collection of components in this container as a {@link Ext.util.MixedCollection}
18531          * @type MixedCollection
18532          * @property items
18533          */
18534         var items = this.items;
18535         if(items){
18536             delete this.items;
18537             this.add(items);
18538         }
18539     },
18540
18541     // private
18542     initItems : function(){
18543         if(!this.items){
18544             this.items = new Ext.util.MixedCollection(false, this.getComponentId);
18545             this.getLayout(); // initialize the layout
18546         }
18547     },
18548
18549     // private
18550     setLayout : function(layout){
18551         if(this.layout && this.layout != layout){
18552             this.layout.setContainer(null);
18553         }
18554         this.initItems();
18555         this.layout = layout;
18556         layout.setContainer(this);
18557     },
18558
18559     afterRender: function(){
18560         // Render this Container, this should be done before setLayout is called which
18561         // will hook onResize
18562         Ext.Container.superclass.afterRender.call(this);
18563         if(!this.layout){
18564             this.layout = 'auto';
18565         }
18566         if(Ext.isObject(this.layout) && !this.layout.layout){
18567             this.layoutConfig = this.layout;
18568             this.layout = this.layoutConfig.type;
18569         }
18570         if(Ext.isString(this.layout)){
18571             this.layout = new Ext.Container.LAYOUTS[this.layout.toLowerCase()](this.layoutConfig);
18572         }
18573         this.setLayout(this.layout);
18574
18575         // If a CardLayout, the active item set
18576         if(this.activeItem !== undefined){
18577             var item = this.activeItem;
18578             delete this.activeItem;
18579             this.layout.setActiveItem(item);
18580         }
18581
18582         // If we have no ownerCt, render and size all children
18583         if(!this.ownerCt){
18584             this.doLayout(false, true);
18585         }
18586
18587         // This is a manually configured flag set by users in conjunction with renderTo.
18588         // Not to be confused with the flag by the same name used in Layouts.
18589         if(this.monitorResize === true){
18590             Ext.EventManager.onWindowResize(this.doLayout, this, [false]);
18591         }
18592     },
18593
18594     /**
18595      * <p>Returns the Element to be used to contain the child Components of this Container.</p>
18596      * <p>An implementation is provided which returns the Container's {@link #getEl Element}, but
18597      * if there is a more complex structure to a Container, this may be overridden to return
18598      * the element into which the {@link #layout layout} renders child Components.</p>
18599      * @return {Ext.Element} The Element to render child Components into.
18600      */
18601     getLayoutTarget : function(){
18602         return this.el;
18603     },
18604
18605     // private - used as the key lookup function for the items collection
18606     getComponentId : function(comp){
18607         return comp.getItemId();
18608     },
18609
18610     /**
18611      * <p>Adds {@link Ext.Component Component}(s) to this Container.</p>
18612      * <br><p><b>Description</b></u> :
18613      * <div><ul class="mdetail-params">
18614      * <li>Fires the {@link #beforeadd} event before adding</li>
18615      * <li>The Container's {@link #defaults default config values} will be applied
18616      * accordingly (see <code>{@link #defaults}</code> for details).</li>
18617      * <li>Fires the {@link #add} event after the component has been added.</li>
18618      * </ul></div>
18619      * <br><p><b>Notes</b></u> :
18620      * <div><ul class="mdetail-params">
18621      * <li>If the Container is <i>already rendered</i> when <code>add</code>
18622      * is called, you may need to call {@link #doLayout} to refresh the view which causes
18623      * any unrendered child Components to be rendered. This is required so that you can
18624      * <code>add</code> multiple child components if needed while only refreshing the layout
18625      * once. For example:<pre><code>
18626 var tb = new {@link Ext.Toolbar}();
18627 tb.render(document.body);  // toolbar is rendered
18628 tb.add({text:'Button 1'}); // add multiple items ({@link #defaultType} for {@link Ext.Toolbar Toolbar} is 'button')
18629 tb.add({text:'Button 2'});
18630 tb.{@link #doLayout}();             // refresh the layout
18631      * </code></pre></li>
18632      * <li><i>Warning:</i> Containers directly managed by the BorderLayout layout manager
18633      * may not be removed or added.  See the Notes for {@link Ext.layout.BorderLayout BorderLayout}
18634      * for more details.</li>
18635      * </ul></div>
18636      * @param {Object/Array} component
18637      * <p>Either a single component or an Array of components to add.  See
18638      * <code>{@link #items}</code> for additional information.</p>
18639      * @param {Object} (Optional) component_2
18640      * @param {Object} (Optional) component_n
18641      * @return {Ext.Component} component The Component (or config object) that was added.
18642      */
18643     add : function(comp){
18644         this.initItems();
18645         var args = arguments.length > 1;
18646         if(args || Ext.isArray(comp)){
18647             var result = [];
18648             Ext.each(args ? arguments : comp, function(c){
18649                 result.push(this.add(c));
18650             }, this);
18651             return result;
18652         }
18653         var c = this.lookupComponent(this.applyDefaults(comp));
18654         var index = this.items.length;
18655         if(this.fireEvent('beforeadd', this, c, index) !== false && this.onBeforeAdd(c) !== false){
18656             this.items.add(c);
18657             // *onAdded
18658             c.onAdded(this, index);
18659             this.onAdd(c);
18660             this.fireEvent('add', this, c, index);
18661         }
18662         return c;
18663     },
18664
18665     onAdd : function(c){
18666         // Empty template method
18667     },
18668
18669     // private
18670     onAdded : function(container, pos) {
18671         //overridden here so we can cascade down, not worth creating a template method.
18672         this.ownerCt = container;
18673         this.initRef();
18674         //initialize references for child items
18675         this.cascade(function(c){
18676             c.initRef();
18677         });
18678         this.fireEvent('added', this, container, pos);
18679     },
18680
18681     /**
18682      * Inserts a Component into this Container at a specified index. Fires the
18683      * {@link #beforeadd} event before inserting, then fires the {@link #add} event after the
18684      * Component has been inserted.
18685      * @param {Number} index The index at which the Component will be inserted
18686      * into the Container's items collection
18687      * @param {Ext.Component} component The child Component to insert.<br><br>
18688      * Ext uses lazy rendering, and will only render the inserted Component should
18689      * it become necessary.<br><br>
18690      * A Component config object may be passed in order to avoid the overhead of
18691      * constructing a real Component object if lazy rendering might mean that the
18692      * inserted Component will not be rendered immediately. To take advantage of
18693      * this 'lazy instantiation', set the {@link Ext.Component#xtype} config
18694      * property to the registered type of the Component wanted.<br><br>
18695      * For a list of all available xtypes, see {@link Ext.Component}.
18696      * @return {Ext.Component} component The Component (or config object) that was
18697      * inserted with the Container's default config values applied.
18698      */
18699     insert : function(index, comp){
18700         this.initItems();
18701         var a = arguments, len = a.length;
18702         if(len > 2){
18703             var result = [];
18704             for(var i = len-1; i >= 1; --i) {
18705                 result.push(this.insert(index, a[i]));
18706             }
18707             return result;
18708         }
18709         var c = this.lookupComponent(this.applyDefaults(comp));
18710         index = Math.min(index, this.items.length);
18711         if(this.fireEvent('beforeadd', this, c, index) !== false && this.onBeforeAdd(c) !== false){
18712             if(c.ownerCt == this){
18713                 this.items.remove(c);
18714             }
18715             this.items.insert(index, c);
18716             c.onAdded(this, index);
18717             this.onAdd(c);
18718             this.fireEvent('add', this, c, index);
18719         }
18720         return c;
18721     },
18722
18723     // private
18724     applyDefaults : function(c){
18725         var d = this.defaults;
18726         if(d){
18727             if(Ext.isFunction(d)){
18728                 d = d.call(this, c);
18729             }
18730             if(Ext.isString(c)){
18731                 c = Ext.ComponentMgr.get(c);
18732                 Ext.apply(c, d);
18733             }else if(!c.events){
18734                 Ext.applyIf(c, d);
18735             }else{
18736                 Ext.apply(c, d);
18737             }
18738         }
18739         return c;
18740     },
18741
18742     // private
18743     onBeforeAdd : function(item){
18744         if(item.ownerCt){
18745             item.ownerCt.remove(item, false);
18746         }
18747         if(this.hideBorders === true){
18748             item.border = (item.border === true);
18749         }
18750     },
18751
18752     /**
18753      * Removes a component from this container.  Fires the {@link #beforeremove} event before removing, then fires
18754      * the {@link #remove} event after the component has been removed.
18755      * @param {Component/String} component The component reference or id to remove.
18756      * @param {Boolean} autoDestroy (optional) True to automatically invoke the removed Component's {@link Ext.Component#destroy} function.
18757      * Defaults to the value of this Container's {@link #autoDestroy} config.
18758      * @return {Ext.Component} component The Component that was removed.
18759      */
18760     remove : function(comp, autoDestroy){
18761         this.initItems();
18762         var c = this.getComponent(comp);
18763         if(c && this.fireEvent('beforeremove', this, c) !== false){
18764             this.doRemove(c, autoDestroy);
18765             this.fireEvent('remove', this, c);
18766         }
18767         return c;
18768     },
18769
18770     onRemove: function(c){
18771         // Empty template method
18772     },
18773
18774     // private
18775     doRemove: function(c, autoDestroy){
18776         var l = this.layout,
18777             hasLayout = l && this.rendered;
18778
18779         if(hasLayout){
18780             l.onRemove(c);
18781         }
18782         this.items.remove(c);
18783         c.onRemoved();
18784         this.onRemove(c);
18785         if(autoDestroy === true || (autoDestroy !== false && this.autoDestroy)){
18786             c.destroy();
18787         }
18788         if(hasLayout){
18789             l.afterRemove(c);
18790         }
18791     },
18792
18793     /**
18794      * Removes all components from this container.
18795      * @param {Boolean} autoDestroy (optional) True to automatically invoke the removed Component's {@link Ext.Component#destroy} function.
18796      * Defaults to the value of this Container's {@link #autoDestroy} config.
18797      * @return {Array} Array of the destroyed components
18798      */
18799     removeAll: function(autoDestroy){
18800         this.initItems();
18801         var item, rem = [], items = [];
18802         this.items.each(function(i){
18803             rem.push(i);
18804         });
18805         for (var i = 0, len = rem.length; i < len; ++i){
18806             item = rem[i];
18807             this.remove(item, autoDestroy);
18808             if(item.ownerCt !== this){
18809                 items.push(item);
18810             }
18811         }
18812         return items;
18813     },
18814
18815     /**
18816      * Examines this container's <code>{@link #items}</code> <b>property</b>
18817      * and gets a direct child component of this container.
18818      * @param {String/Number} comp This parameter may be any of the following:
18819      * <div><ul class="mdetail-params">
18820      * <li>a <b><code>String</code></b> : representing the <code>{@link Ext.Component#itemId itemId}</code>
18821      * or <code>{@link Ext.Component#id id}</code> of the child component </li>
18822      * <li>a <b><code>Number</code></b> : representing the position of the child component
18823      * within the <code>{@link #items}</code> <b>property</b></li>
18824      * </ul></div>
18825      * <p>For additional information see {@link Ext.util.MixedCollection#get}.
18826      * @return Ext.Component The component (if found).
18827      */
18828     getComponent : function(comp){
18829         if(Ext.isObject(comp)){
18830             comp = comp.getItemId();
18831         }
18832         return this.items.get(comp);
18833     },
18834
18835     // private
18836     lookupComponent : function(comp){
18837         if(Ext.isString(comp)){
18838             return Ext.ComponentMgr.get(comp);
18839         }else if(!comp.events){
18840             return this.createComponent(comp);
18841         }
18842         return comp;
18843     },
18844
18845     // private
18846     createComponent : function(config, defaultType){
18847         // add in ownerCt at creation time but then immediately
18848         // remove so that onBeforeAdd can handle it
18849         var c = config.render ? config : Ext.create(Ext.apply({
18850             ownerCt: this
18851         }, config), defaultType || this.defaultType);
18852         delete c.ownerCt;
18853         return c;
18854     },
18855
18856     /**
18857     * We can only lay out if there is a view area in which to layout.
18858     * display:none on the layout target, *or any of its parent elements* will mean it has no view area.
18859     */
18860
18861     // private
18862     canLayout : function() {
18863         var el = this.getVisibilityEl();
18864         return el && el.dom && !el.isStyle("display", "none");
18865     },
18866
18867     /**
18868      * Force this container's layout to be recalculated. A call to this function is required after adding a new component
18869      * to an already rendered container, or possibly after changing sizing/position properties of child components.
18870      * @param {Boolean} shallow (optional) True to only calc the layout of this component, and let child components auto
18871      * calc layouts as required (defaults to false, which calls doLayout recursively for each subcontainer)
18872      * @param {Boolean} force (optional) True to force a layout to occur, even if the item is hidden.
18873      * @return {Ext.Container} this
18874      */
18875
18876     doLayout : function(shallow, force){
18877         var rendered = this.rendered,
18878             forceLayout = force || this.forceLayout;
18879
18880         if(this.collapsed || !this.canLayout()){
18881             this.deferLayout = this.deferLayout || !shallow;
18882             if(!forceLayout){
18883                 return;
18884             }
18885             shallow = shallow && !this.deferLayout;
18886         } else {
18887             delete this.deferLayout;
18888         }
18889         if(rendered && this.layout){
18890             this.layout.layout();
18891         }
18892         if(shallow !== true && this.items){
18893             var cs = this.items.items;
18894             for(var i = 0, len = cs.length; i < len; i++){
18895                 var c = cs[i];
18896                 if(c.doLayout){
18897                     c.doLayout(false, forceLayout);
18898                 }
18899             }
18900         }
18901         if(rendered){
18902             this.onLayout(shallow, forceLayout);
18903         }
18904         // Initial layout completed
18905         this.hasLayout = true;
18906         delete this.forceLayout;
18907     },
18908
18909     onLayout : Ext.emptyFn,
18910
18911     // private
18912     shouldBufferLayout: function(){
18913         /*
18914          * Returns true if the container should buffer a layout.
18915          * This is true only if the container has previously been laid out
18916          * and has a parent container that is pending a layout.
18917          */
18918         var hl = this.hasLayout;
18919         if(this.ownerCt){
18920             // Only ever buffer if we've laid out the first time and we have one pending.
18921             return hl ? !this.hasLayoutPending() : false;
18922         }
18923         // Never buffer initial layout
18924         return hl;
18925     },
18926
18927     // private
18928     hasLayoutPending: function(){
18929         // Traverse hierarchy to see if any parent container has a pending layout.
18930         var pending = false;
18931         this.ownerCt.bubble(function(c){
18932             if(c.layoutPending){
18933                 pending = true;
18934                 return false;
18935             }
18936         });
18937         return pending;
18938     },
18939
18940     onShow : function(){
18941         // removes css classes that were added to hide
18942         Ext.Container.superclass.onShow.call(this);
18943         // If we were sized during the time we were hidden, layout.
18944         if(Ext.isDefined(this.deferLayout)){
18945             delete this.deferLayout;
18946             this.doLayout(true);
18947         }
18948     },
18949
18950     /**
18951      * Returns the layout currently in use by the container.  If the container does not currently have a layout
18952      * set, a default {@link Ext.layout.ContainerLayout} will be created and set as the container's layout.
18953      * @return {ContainerLayout} layout The container's layout
18954      */
18955     getLayout : function(){
18956         if(!this.layout){
18957             var layout = new Ext.layout.ContainerLayout(this.layoutConfig);
18958             this.setLayout(layout);
18959         }
18960         return this.layout;
18961     },
18962
18963     // private
18964     beforeDestroy : function(){
18965         var c;
18966         if(this.items){
18967             while(c = this.items.first()){
18968                 this.doRemove(c, true);
18969             }
18970         }
18971         if(this.monitorResize){
18972             Ext.EventManager.removeResizeListener(this.doLayout, this);
18973         }
18974         Ext.destroy(this.layout);
18975         Ext.Container.superclass.beforeDestroy.call(this);
18976     },
18977
18978     /**
18979      * Bubbles up the component/container heirarchy, calling the specified function with each component. The scope (<i>this</i>) of
18980      * function call will be the scope provided or the current component. The arguments to the function
18981      * will be the args provided or the current component. If the function returns false at any point,
18982      * the bubble is stopped.
18983      * @param {Function} fn The function to call
18984      * @param {Object} scope (optional) The scope of the function (defaults to current node)
18985      * @param {Array} args (optional) The args to call the function with (default to passing the current component)
18986      * @return {Ext.Container} this
18987      */
18988     bubble : function(fn, scope, args){
18989         var p = this;
18990         while(p){
18991             if(fn.apply(scope || p, args || [p]) === false){
18992                 break;
18993             }
18994             p = p.ownerCt;
18995         }
18996         return this;
18997     },
18998
18999     /**
19000      * Cascades down the component/container heirarchy from this component (called first), calling the specified function with
19001      * each component. The scope (<i>this</i>) of
19002      * function call will be the scope provided or the current component. The arguments to the function
19003      * will be the args provided or the current component. If the function returns false at any point,
19004      * the cascade is stopped on that branch.
19005      * @param {Function} fn The function to call
19006      * @param {Object} scope (optional) The scope of the function (defaults to current component)
19007      * @param {Array} args (optional) The args to call the function with (defaults to passing the current component)
19008      * @return {Ext.Container} this
19009      */
19010     cascade : function(fn, scope, args){
19011         if(fn.apply(scope || this, args || [this]) !== false){
19012             if(this.items){
19013                 var cs = this.items.items;
19014                 for(var i = 0, len = cs.length; i < len; i++){
19015                     if(cs[i].cascade){
19016                         cs[i].cascade(fn, scope, args);
19017                     }else{
19018                         fn.apply(scope || cs[i], args || [cs[i]]);
19019                     }
19020                 }
19021             }
19022         }
19023         return this;
19024     },
19025
19026     /**
19027      * Find a component under this container at any level by id
19028      * @param {String} id
19029      * @return Ext.Component
19030      */
19031     findById : function(id){
19032         var m, ct = this;
19033         this.cascade(function(c){
19034             if(ct != c && c.id === id){
19035                 m = c;
19036                 return false;
19037             }
19038         });
19039         return m || null;
19040     },
19041
19042     /**
19043      * Find a component under this container at any level by xtype or class
19044      * @param {String/Class} xtype The xtype string for a component, or the class of the component directly
19045      * @param {Boolean} shallow (optional) False to check whether this Component is descended from the xtype (this is
19046      * the default), or true to check whether this Component is directly of the specified xtype.
19047      * @return {Array} Array of Ext.Components
19048      */
19049     findByType : function(xtype, shallow){
19050         return this.findBy(function(c){
19051             return c.isXType(xtype, shallow);
19052         });
19053     },
19054
19055     /**
19056      * Find a component under this container at any level by property
19057      * @param {String} prop
19058      * @param {String} value
19059      * @return {Array} Array of Ext.Components
19060      */
19061     find : function(prop, value){
19062         return this.findBy(function(c){
19063             return c[prop] === value;
19064         });
19065     },
19066
19067     /**
19068      * Find a component under this container at any level by a custom function. If the passed function returns
19069      * true, the component will be included in the results. The passed function is called with the arguments (component, this container).
19070      * @param {Function} fn The function to call
19071      * @param {Object} scope (optional)
19072      * @return {Array} Array of Ext.Components
19073      */
19074     findBy : function(fn, scope){
19075         var m = [], ct = this;
19076         this.cascade(function(c){
19077             if(ct != c && fn.call(scope || c, c, ct) === true){
19078                 m.push(c);
19079             }
19080         });
19081         return m;
19082     },
19083
19084     /**
19085      * Get a component contained by this container (alias for items.get(key))
19086      * @param {String/Number} key The index or id of the component
19087      * @return {Ext.Component} Ext.Component
19088      */
19089     get : function(key){
19090         return this.items.get(key);
19091     }
19092 });
19093
19094 Ext.Container.LAYOUTS = {};
19095 Ext.reg('container', Ext.Container);
19096 /**
19097  * @class Ext.layout.ContainerLayout
19098  * <p>This class is intended to be extended or created via the <tt><b>{@link Ext.Container#layout layout}</b></tt>
19099  * configuration property.  See <tt><b>{@link Ext.Container#layout}</b></tt> for additional details.</p>
19100  */
19101 Ext.layout.ContainerLayout = Ext.extend(Object, {
19102     /**
19103      * @cfg {String} extraCls
19104      * <p>An optional extra CSS class that will be added to the container. This can be useful for adding
19105      * customized styles to the container or any of its children using standard CSS rules. See
19106      * {@link Ext.Component}.{@link Ext.Component#ctCls ctCls} also.</p>
19107      * <p><b>Note</b>: <tt>extraCls</tt> defaults to <tt>''</tt> except for the following classes
19108      * which assign a value by default:
19109      * <div class="mdetail-params"><ul>
19110      * <li>{@link Ext.layout.AbsoluteLayout Absolute Layout} : <tt>'x-abs-layout-item'</tt></li>
19111      * <li>{@link Ext.layout.Box Box Layout} : <tt>'x-box-item'</tt></li>
19112      * <li>{@link Ext.layout.ColumnLayout Column Layout} : <tt>'x-column'</tt></li>
19113      * </ul></div>
19114      * To configure the above Classes with an extra CSS class append to the default.  For example,
19115      * for ColumnLayout:<pre><code>
19116      * extraCls: 'x-column custom-class'
19117      * </code></pre>
19118      * </p>
19119      */
19120     /**
19121      * @cfg {Boolean} renderHidden
19122      * True to hide each contained item on render (defaults to false).
19123      */
19124
19125     /**
19126      * A reference to the {@link Ext.Component} that is active.  For example, <pre><code>
19127      * if(myPanel.layout.activeItem.id == 'item-1') { ... }
19128      * </code></pre>
19129      * <tt>activeItem</tt> only applies to layout styles that can display items one at a time
19130      * (like {@link Ext.layout.AccordionLayout}, {@link Ext.layout.CardLayout}
19131      * and {@link Ext.layout.FitLayout}).  Read-only.  Related to {@link Ext.Container#activeItem}.
19132      * @type {Ext.Component}
19133      * @property activeItem
19134      */
19135
19136     // private
19137     monitorResize:false,
19138     // private
19139     activeItem : null,
19140
19141     constructor : function(config){
19142         this.id = Ext.id(null, 'ext-layout-');
19143         Ext.apply(this, config);
19144     },
19145
19146     type: 'container',
19147
19148     /* Workaround for how IE measures autoWidth elements.  It prefers bottom-up measurements
19149       whereas other browser prefer top-down.  We will hide all target child elements before we measure and
19150       put them back to get an accurate measurement.
19151     */
19152     IEMeasureHack : function(target, viewFlag) {
19153         var tChildren = target.dom.childNodes, tLen = tChildren.length, c, d = [], e, i, ret;
19154         for (i = 0 ; i < tLen ; i++) {
19155             c = tChildren[i];
19156             e = Ext.get(c);
19157             if (e) {
19158                 d[i] = e.getStyle('display');
19159                 e.setStyle({display: 'none'});
19160             }
19161         }
19162         ret = target ? target.getViewSize(viewFlag) : {};
19163         for (i = 0 ; i < tLen ; i++) {
19164             c = tChildren[i];
19165             e = Ext.get(c);
19166             if (e) {
19167                 e.setStyle({display: d[i]});
19168             }
19169         }
19170         return ret;
19171     },
19172
19173     // Placeholder for the derived layouts
19174     getLayoutTargetSize : Ext.EmptyFn,
19175
19176     // private
19177     layout : function(){
19178         var ct = this.container, target = ct.getLayoutTarget();
19179         if(!(this.hasLayout || Ext.isEmpty(this.targetCls))){
19180             target.addClass(this.targetCls);
19181         }
19182         this.onLayout(ct, target);
19183         ct.fireEvent('afterlayout', ct, this);
19184     },
19185
19186     // private
19187     onLayout : function(ct, target){
19188         this.renderAll(ct, target);
19189     },
19190
19191     // private
19192     isValidParent : function(c, target){
19193         return target && c.getPositionEl().dom.parentNode == (target.dom || target);
19194     },
19195
19196     // private
19197     renderAll : function(ct, target){
19198         var items = ct.items.items, i, c, len = items.length;
19199         for(i = 0; i < len; i++) {
19200             c = items[i];
19201             if(c && (!c.rendered || !this.isValidParent(c, target))){
19202                 this.renderItem(c, i, target);
19203             }
19204         }
19205     },
19206
19207     // private
19208     renderItem : function(c, position, target){
19209         if(c){
19210             if(!c.rendered){
19211                 c.render(target, position);
19212                 this.configureItem(c, position);
19213             }else if(!this.isValidParent(c, target)){
19214                 if(Ext.isNumber(position)){
19215                     position = target.dom.childNodes[position];
19216                 }
19217                 target.dom.insertBefore(c.getPositionEl().dom, position || null);
19218                 c.container = target;
19219                 this.configureItem(c, position);
19220             }
19221         }
19222     },
19223
19224     // private.
19225     // Get all rendered items to lay out.
19226     getRenderedItems: function(ct){
19227         var t = ct.getLayoutTarget(), cti = ct.items.items, len = cti.length, i, c, items = [];
19228         for (i = 0; i < len; i++) {
19229             if((c = cti[i]).rendered && this.isValidParent(c, t)){
19230                 items.push(c);
19231             }
19232         };
19233         return items;
19234     },
19235
19236     // private
19237     configureItem: function(c, position){
19238         if(this.extraCls){
19239             var t = c.getPositionEl ? c.getPositionEl() : c;
19240             t.addClass(this.extraCls);
19241         }
19242         // If we are forcing a layout, do so *before* we hide so elements have height/width
19243         if(c.doLayout && this.forceLayout){
19244             c.doLayout();
19245         }
19246         if (this.renderHidden && c != this.activeItem) {
19247             c.hide();
19248         }
19249     },
19250
19251     onRemove: function(c){
19252          if(this.activeItem == c){
19253             delete this.activeItem;
19254          }
19255          if(c.rendered && this.extraCls){
19256             var t = c.getPositionEl ? c.getPositionEl() : c;
19257             t.removeClass(this.extraCls);
19258         }
19259     },
19260
19261     afterRemove: function(c){
19262         if(c.removeRestore){
19263             c.removeMode = 'container';
19264             delete c.removeRestore;
19265         }
19266     },
19267
19268     // private
19269     onResize: function(){
19270         var ct = this.container,
19271             b;
19272         if(ct.collapsed){
19273             return;
19274         }
19275         if(b = ct.bufferResize){
19276             // Only allow if we should buffer the layout
19277             if(ct.shouldBufferLayout()){
19278                 if(!this.resizeTask){
19279                     this.resizeTask = new Ext.util.DelayedTask(this.runLayout, this);
19280                     this.resizeBuffer = Ext.isNumber(b) ? b : 50;
19281                 }
19282                 ct.layoutPending = true;
19283                 this.resizeTask.delay(this.resizeBuffer);
19284             }
19285         }else{
19286             this.runLayout();
19287         }
19288     },
19289
19290     runLayout: function(){
19291         var ct = this.container;
19292         // AutoLayout is known to require the recursive doLayout call, others need this currently (BorderLayout for example)
19293         // but shouldn't.  A more extensive review will take place for 3.2 which requires a ContainerMgr with hierarchy lookups.
19294         //this.layout();
19295         //ct.onLayout();
19296         ct.doLayout();
19297         delete ct.layoutPending;
19298     },
19299
19300     // private
19301     setContainer : function(ct){
19302         if (!Ext.LayoutManager) {
19303             Ext.LayoutManager = {};
19304         }
19305
19306         /* This monitorResize flag will be renamed soon as to avoid confusion
19307         * with the Container version which hooks onWindowResize to doLayout
19308         *
19309         * monitorResize flag in this context attaches the resize event between
19310         * a container and it's layout
19311         */
19312
19313         if(this.monitorResize && ct != this.container){
19314             var old = this.container;
19315             if(old){
19316                 old.un(old.resizeEvent, this.onResize, this);
19317             }
19318             if(ct){
19319                 ct.on(ct.resizeEvent, this.onResize, this);
19320             }
19321         }
19322         this.container = ct;
19323     },
19324
19325     // private
19326     parseMargins : function(v){
19327         if(Ext.isNumber(v)){
19328             v = v.toString();
19329         }
19330         var ms = v.split(' ');
19331         var len = ms.length;
19332         if(len == 1){
19333             ms[1] = ms[2] = ms[3] = ms[0];
19334         } else if(len == 2){
19335             ms[2] = ms[0];
19336             ms[3] = ms[1];
19337         } else if(len == 3){
19338             ms[3] = ms[1];
19339         }
19340         return {
19341             top:parseInt(ms[0], 10) || 0,
19342             right:parseInt(ms[1], 10) || 0,
19343             bottom:parseInt(ms[2], 10) || 0,
19344             left:parseInt(ms[3], 10) || 0
19345         };
19346     },
19347
19348     /**
19349      * The {@link Ext.Template Ext.Template} used by Field rendering layout classes (such as
19350      * {@link Ext.layout.FormLayout}) to create the DOM structure of a fully wrapped,
19351      * labeled and styled form Field. A default Template is supplied, but this may be
19352      * overriden to create custom field structures. The template processes values returned from
19353      * {@link Ext.layout.FormLayout#getTemplateArgs}.
19354      * @property fieldTpl
19355      * @type Ext.Template
19356      */
19357     fieldTpl: (function() {
19358         var t = new Ext.Template(
19359             '<div class="x-form-item {itemCls}" tabIndex="-1">',
19360                 '<label for="{id}" style="{labelStyle}" class="x-form-item-label">{label}{labelSeparator}</label>',
19361                 '<div class="x-form-element" id="x-form-el-{id}" style="{elementStyle}">',
19362                 '</div><div class="{clearCls}"></div>',
19363             '</div>'
19364         );
19365         t.disableFormats = true;
19366         return t.compile();
19367     })(),
19368
19369     /*
19370      * Destroys this layout. This is a template method that is empty by default, but should be implemented
19371      * by subclasses that require explicit destruction to purge event handlers or remove DOM nodes.
19372      * @protected
19373      */
19374     destroy : function(){
19375         if(!Ext.isEmpty(this.targetCls)){
19376             var target = this.container.getLayoutTarget();
19377             if(target){
19378                 target.removeClass(this.targetCls);
19379             }
19380         }
19381     }
19382 });/**
19383  * @class Ext.layout.AutoLayout
19384  * <p>The AutoLayout is the default layout manager delegated by {@link Ext.Container} to
19385  * render any child Components when no <tt>{@link Ext.Container#layout layout}</tt> is configured into
19386  * a {@link Ext.Container Container}. ContainerLayout provides the basic foundation for all other layout
19387  * classes in Ext. It simply renders all child Components into the Container, performing no sizing or
19388  * positioning services. To utilize a layout that provides sizing and positioning of child Components,
19389  * specify an appropriate <tt>{@link Ext.Container#layout layout}</tt>.</p>
19390  */
19391 Ext.layout.AutoLayout = Ext.extend(Ext.layout.ContainerLayout, {
19392     runLayout: function(){
19393         var ct = this.container;
19394         ct.doLayout();
19395         delete ct.layoutPending;
19396     }
19397 });
19398
19399 Ext.Container.LAYOUTS['auto'] = Ext.layout.AutoLayout;
19400 /**\r
19401  * @class Ext.layout.FitLayout\r
19402  * @extends Ext.layout.ContainerLayout\r
19403  * <p>This is a base class for layouts that contain <b>a single item</b> that automatically expands to fill the layout's\r
19404  * container.  This class is intended to be extended or created via the <tt>layout:'fit'</tt> {@link Ext.Container#layout}\r
19405  * config, and should generally not need to be created directly via the new keyword.</p>\r
19406  * <p>FitLayout does not have any direct config options (other than inherited ones).  To fit a panel to a container\r
19407  * using FitLayout, simply set layout:'fit' on the container and add a single panel to it.  If the container has\r
19408  * multiple panels, only the first one will be displayed.  Example usage:</p>\r
19409  * <pre><code>\r
19410 var p = new Ext.Panel({\r
19411     title: 'Fit Layout',\r
19412     layout:'fit',\r
19413     items: {\r
19414         title: 'Inner Panel',\r
19415         html: '&lt;p&gt;This is the inner panel content&lt;/p&gt;',\r
19416         border: false\r
19417     }\r
19418 });\r
19419 </code></pre>\r
19420  */\r
19421 Ext.layout.FitLayout = Ext.extend(Ext.layout.ContainerLayout, {\r
19422     // private\r
19423     monitorResize:true,\r
19424 \r
19425     type: 'fit',\r
19426 \r
19427     getLayoutTargetSize : function() {\r
19428         var target = this.container.getLayoutTarget();\r
19429         if (!target) {\r
19430             return {};\r
19431         }\r
19432         // Style Sized (scrollbars not included)\r
19433         return target.getStyleSize();\r
19434     },\r
19435 \r
19436     // private\r
19437     onLayout : function(ct, target){\r
19438         Ext.layout.FitLayout.superclass.onLayout.call(this, ct, target);\r
19439         if(!ct.collapsed){\r
19440             this.setItemSize(this.activeItem || ct.items.itemAt(0), this.getLayoutTargetSize());\r
19441         }\r
19442     },\r
19443 \r
19444     // private\r
19445     setItemSize : function(item, size){\r
19446         if(item && size.height > 0){ // display none?\r
19447             item.setSize(size);\r
19448         }\r
19449     }\r
19450 });\r
19451 Ext.Container.LAYOUTS['fit'] = Ext.layout.FitLayout;/**\r
19452  * @class Ext.layout.CardLayout\r
19453  * @extends Ext.layout.FitLayout\r
19454  * <p>This layout manages multiple child Components, each fitted to the Container, where only a single child Component can be\r
19455  * visible at any given time.  This layout style is most commonly used for wizards, tab implementations, etc.\r
19456  * This class is intended to be extended or created via the layout:'card' {@link Ext.Container#layout} config,\r
19457  * and should generally not need to be created directly via the new keyword.</p>\r
19458  * <p>The CardLayout's focal method is {@link #setActiveItem}.  Since only one panel is displayed at a time,\r
19459  * the only way to move from one Component to the next is by calling setActiveItem, passing the id or index of\r
19460  * the next panel to display.  The layout itself does not provide a user interface for handling this navigation,\r
19461  * so that functionality must be provided by the developer.</p>\r
19462  * <p>In the following example, a simplistic wizard setup is demonstrated.  A button bar is added\r
19463  * to the footer of the containing panel to provide navigation buttons.  The buttons will be handled by a\r
19464  * common navigation routine -- for this example, the implementation of that routine has been ommitted since\r
19465  * it can be any type of custom logic.  Note that other uses of a CardLayout (like a tab control) would require a\r
19466  * completely different implementation.  For serious implementations, a better approach would be to extend\r
19467  * CardLayout to provide the custom functionality needed.  Example usage:</p>\r
19468  * <pre><code>\r
19469 var navHandler = function(direction){\r
19470     // This routine could contain business logic required to manage the navigation steps.\r
19471     // It would call setActiveItem as needed, manage navigation button state, handle any\r
19472     // branching logic that might be required, handle alternate actions like cancellation\r
19473     // or finalization, etc.  A complete wizard implementation could get pretty\r
19474     // sophisticated depending on the complexity required, and should probably be\r
19475     // done as a subclass of CardLayout in a real-world implementation.\r
19476 };\r
19477 \r
19478 var card = new Ext.Panel({\r
19479     title: 'Example Wizard',\r
19480     layout:'card',\r
19481     activeItem: 0, // make sure the active item is set on the container config!\r
19482     bodyStyle: 'padding:15px',\r
19483     defaults: {\r
19484         // applied to each contained panel\r
19485         border:false\r
19486     },\r
19487     // just an example of one possible navigation scheme, using buttons\r
19488     bbar: [\r
19489         {\r
19490             id: 'move-prev',\r
19491             text: 'Back',\r
19492             handler: navHandler.createDelegate(this, [-1]),\r
19493             disabled: true\r
19494         },\r
19495         '->', // greedy spacer so that the buttons are aligned to each side\r
19496         {\r
19497             id: 'move-next',\r
19498             text: 'Next',\r
19499             handler: navHandler.createDelegate(this, [1])\r
19500         }\r
19501     ],\r
19502     // the panels (or "cards") within the layout\r
19503     items: [{\r
19504         id: 'card-0',\r
19505         html: '&lt;h1&gt;Welcome to the Wizard!&lt;/h1&gt;&lt;p&gt;Step 1 of 3&lt;/p&gt;'\r
19506     },{\r
19507         id: 'card-1',\r
19508         html: '&lt;p&gt;Step 2 of 3&lt;/p&gt;'\r
19509     },{\r
19510         id: 'card-2',\r
19511         html: '&lt;h1&gt;Congratulations!&lt;/h1&gt;&lt;p&gt;Step 3 of 3 - Complete&lt;/p&gt;'\r
19512     }]\r
19513 });\r
19514 </code></pre>\r
19515  */\r
19516 Ext.layout.CardLayout = Ext.extend(Ext.layout.FitLayout, {\r
19517     /**\r
19518      * @cfg {Boolean} deferredRender\r
19519      * True to render each contained item at the time it becomes active, false to render all contained items\r
19520      * as soon as the layout is rendered (defaults to false).  If there is a significant amount of content or\r
19521      * a lot of heavy controls being rendered into panels that are not displayed by default, setting this to\r
19522      * true might improve performance.\r
19523      */\r
19524     deferredRender : false,\r
19525 \r
19526     /**\r
19527      * @cfg {Boolean} layoutOnCardChange\r
19528      * True to force a layout of the active item when the active card is changed. Defaults to false.\r
19529      */\r
19530     layoutOnCardChange : false,\r
19531 \r
19532     /**\r
19533      * @cfg {Boolean} renderHidden @hide\r
19534      */\r
19535     // private\r
19536     renderHidden : true,\r
19537 \r
19538     type: 'card',\r
19539 \r
19540     constructor: function(config){\r
19541         Ext.layout.CardLayout.superclass.constructor.call(this, config);\r
19542     },\r
19543 \r
19544     /**\r
19545      * Sets the active (visible) item in the layout.\r
19546      * @param {String/Number} item The string component id or numeric index of the item to activate\r
19547      */\r
19548     setActiveItem : function(item){\r
19549         var ai = this.activeItem,\r
19550             ct = this.container;\r
19551         item = ct.getComponent(item);\r
19552 \r
19553         // Is this a valid, different card?\r
19554         if(item && ai != item){\r
19555 \r
19556             // Changing cards, hide the current one\r
19557             if(ai){\r
19558                 ai.hide();\r
19559                 if (ai.hidden !== true) {\r
19560                     return false;\r
19561                 }\r
19562                 ai.fireEvent('deactivate', ai);\r
19563             }\r
19564             // Change activeItem reference\r
19565             this.activeItem = item;\r
19566 \r
19567             // The container is about to get a recursive layout, remove any deferLayout reference\r
19568             // because it will trigger a redundant layout.\r
19569             delete item.deferLayout;\r
19570 \r
19571             // Show the new component\r
19572             item.show();\r
19573 \r
19574             this.layout();\r
19575 \r
19576             if(item.doLayout){\r
19577                 item.doLayout();\r
19578             }\r
19579             item.fireEvent('activate', item);\r
19580         }\r
19581     },\r
19582 \r
19583     // private\r
19584     renderAll : function(ct, target){\r
19585         if(this.deferredRender){\r
19586             this.renderItem(this.activeItem, undefined, target);\r
19587         }else{\r
19588             Ext.layout.CardLayout.superclass.renderAll.call(this, ct, target);\r
19589         }\r
19590     }\r
19591 });\r
19592 Ext.Container.LAYOUTS['card'] = Ext.layout.CardLayout;/**
19593  * @class Ext.layout.AnchorLayout
19594  * @extends Ext.layout.ContainerLayout
19595  * <p>This is a layout that enables anchoring of contained elements relative to the container's dimensions.
19596  * If the container is resized, all anchored items are automatically rerendered according to their
19597  * <b><tt>{@link #anchor}</tt></b> rules.</p>
19598  * <p>This class is intended to be extended or created via the layout:'anchor' {@link Ext.Container#layout}
19599  * config, and should generally not need to be created directly via the new keyword.</p>
19600  * <p>AnchorLayout does not have any direct config options (other than inherited ones). By default,
19601  * AnchorLayout will calculate anchor measurements based on the size of the container itself. However, the
19602  * container using the AnchorLayout can supply an anchoring-specific config property of <b>anchorSize</b>.
19603  * If anchorSize is specifed, the layout will use it as a virtual container for the purposes of calculating
19604  * anchor measurements based on it instead, allowing the container to be sized independently of the anchoring
19605  * logic if necessary.  For example:</p>
19606  * <pre><code>
19607 var viewport = new Ext.Viewport({
19608     layout:'anchor',
19609     anchorSize: {width:800, height:600},
19610     items:[{
19611         title:'Item 1',
19612         html:'Content 1',
19613         width:800,
19614         anchor:'right 20%'
19615     },{
19616         title:'Item 2',
19617         html:'Content 2',
19618         width:300,
19619         anchor:'50% 30%'
19620     },{
19621         title:'Item 3',
19622         html:'Content 3',
19623         width:600,
19624         anchor:'-100 50%'
19625     }]
19626 });
19627  * </code></pre>
19628  */
19629 Ext.layout.AnchorLayout = Ext.extend(Ext.layout.ContainerLayout, {
19630     /**
19631      * @cfg {String} anchor
19632      * <p>This configuation option is to be applied to <b>child <tt>items</tt></b> of a container managed by
19633      * this layout (ie. configured with <tt>layout:'anchor'</tt>).</p><br/>
19634      *
19635      * <p>This value is what tells the layout how an item should be anchored to the container. <tt>items</tt>
19636      * added to an AnchorLayout accept an anchoring-specific config property of <b>anchor</b> which is a string
19637      * containing two values: the horizontal anchor value and the vertical anchor value (for example, '100% 50%').
19638      * The following types of anchor values are supported:<div class="mdetail-params"><ul>
19639      *
19640      * <li><b>Percentage</b> : Any value between 1 and 100, expressed as a percentage.<div class="sub-desc">
19641      * The first anchor is the percentage width that the item should take up within the container, and the
19642      * second is the percentage height.  For example:<pre><code>
19643 // two values specified
19644 anchor: '100% 50%' // render item complete width of the container and
19645                    // 1/2 height of the container
19646 // one value specified
19647 anchor: '100%'     // the width value; the height will default to auto
19648      * </code></pre></div></li>
19649      *
19650      * <li><b>Offsets</b> : Any positive or negative integer value.<div class="sub-desc">
19651      * This is a raw adjustment where the first anchor is the offset from the right edge of the container,
19652      * and the second is the offset from the bottom edge. For example:<pre><code>
19653 // two values specified
19654 anchor: '-50 -100' // render item the complete width of the container
19655                    // minus 50 pixels and
19656                    // the complete height minus 100 pixels.
19657 // one value specified
19658 anchor: '-50'      // anchor value is assumed to be the right offset value
19659                    // bottom offset will default to 0
19660      * </code></pre></div></li>
19661      *
19662      * <li><b>Sides</b> : Valid values are <tt>'right'</tt> (or <tt>'r'</tt>) and <tt>'bottom'</tt>
19663      * (or <tt>'b'</tt>).<div class="sub-desc">
19664      * Either the container must have a fixed size or an anchorSize config value defined at render time in
19665      * order for these to have any effect.</div></li>
19666      *
19667      * <li><b>Mixed</b> : <div class="sub-desc">
19668      * Anchor values can also be mixed as needed.  For example, to render the width offset from the container
19669      * right edge by 50 pixels and 75% of the container's height use:
19670      * <pre><code>
19671 anchor: '-50 75%'
19672      * </code></pre></div></li>
19673      *
19674      *
19675      * </ul></div>
19676      */
19677
19678     // private
19679     monitorResize:true,
19680     type: 'anchor',
19681
19682     getLayoutTargetSize : function() {
19683         var target = this.container.getLayoutTarget();
19684         if (!target) {
19685             return {};
19686         }
19687         // Style Sized (scrollbars not included)
19688         return target.getStyleSize();
19689     },
19690
19691     // private
19692     onLayout : function(ct, target){
19693         Ext.layout.AnchorLayout.superclass.onLayout.call(this, ct, target);
19694         var size = this.getLayoutTargetSize();
19695
19696         var w = size.width, h = size.height;
19697
19698         if(w < 20 && h < 20){
19699             return;
19700         }
19701
19702         // find the container anchoring size
19703         var aw, ah;
19704         if(ct.anchorSize){
19705             if(typeof ct.anchorSize == 'number'){
19706                 aw = ct.anchorSize;
19707             }else{
19708                 aw = ct.anchorSize.width;
19709                 ah = ct.anchorSize.height;
19710             }
19711         }else{
19712             aw = ct.initialConfig.width;
19713             ah = ct.initialConfig.height;
19714         }
19715
19716         var cs = this.getRenderedItems(ct), len = cs.length, i, c, a, cw, ch, el, vs;
19717         for(i = 0; i < len; i++){
19718             c = cs[i];
19719             el = c.getPositionEl();
19720             if(c.anchor){
19721                 a = c.anchorSpec;
19722                 if(!a){ // cache all anchor values
19723                     vs = c.anchor.split(' ');
19724                     c.anchorSpec = a = {
19725                         right: this.parseAnchor(vs[0], c.initialConfig.width, aw),
19726                         bottom: this.parseAnchor(vs[1], c.initialConfig.height, ah)
19727                     };
19728                 }
19729                 cw = a.right ? this.adjustWidthAnchor(a.right(w) - el.getMargins('lr'), c) : undefined;
19730                 ch = a.bottom ? this.adjustHeightAnchor(a.bottom(h) - el.getMargins('tb'), c) : undefined;
19731
19732                 if(cw || ch){
19733                     c.setSize(cw || undefined, ch || undefined);
19734                 }
19735             }
19736         }
19737     },
19738
19739     // private
19740     parseAnchor : function(a, start, cstart){
19741         if(a && a != 'none'){
19742             var last;
19743             if(/^(r|right|b|bottom)$/i.test(a)){   // standard anchor
19744                 var diff = cstart - start;
19745                 return function(v){
19746                     if(v !== last){
19747                         last = v;
19748                         return v - diff;
19749                     }
19750                 }
19751             }else if(a.indexOf('%') != -1){
19752                 var ratio = parseFloat(a.replace('%', ''))*.01;   // percentage
19753                 return function(v){
19754                     if(v !== last){
19755                         last = v;
19756                         return Math.floor(v*ratio);
19757                     }
19758                 }
19759             }else{
19760                 a = parseInt(a, 10);
19761                 if(!isNaN(a)){                            // simple offset adjustment
19762                     return function(v){
19763                         if(v !== last){
19764                             last = v;
19765                             return v + a;
19766                         }
19767                     }
19768                 }
19769             }
19770         }
19771         return false;
19772     },
19773
19774     // private
19775     adjustWidthAnchor : function(value, comp){
19776         return value;
19777     },
19778
19779     // private
19780     adjustHeightAnchor : function(value, comp){
19781         return value;
19782     }
19783
19784     /**
19785      * @property activeItem
19786      * @hide
19787      */
19788 });
19789 Ext.Container.LAYOUTS['anchor'] = Ext.layout.AnchorLayout;
19790 /**\r
19791  * @class Ext.layout.ColumnLayout\r
19792  * @extends Ext.layout.ContainerLayout\r
19793  * <p>This is the layout style of choice for creating structural layouts in a multi-column format where the width of\r
19794  * each column can be specified as a percentage or fixed width, but the height is allowed to vary based on the content.\r
19795  * This class is intended to be extended or created via the layout:'column' {@link Ext.Container#layout} config,\r
19796  * and should generally not need to be created directly via the new keyword.</p>\r
19797  * <p>ColumnLayout does not have any direct config options (other than inherited ones), but it does support a\r
19798  * specific config property of <b><tt>columnWidth</tt></b> that can be included in the config of any panel added to it.  The\r
19799  * layout will use the columnWidth (if present) or width of each panel during layout to determine how to size each panel.\r
19800  * If width or columnWidth is not specified for a given panel, its width will default to the panel's width (or auto).</p>\r
19801  * <p>The width property is always evaluated as pixels, and must be a number greater than or equal to 1.\r
19802  * The columnWidth property is always evaluated as a percentage, and must be a decimal value greater than 0 and\r
19803  * less than 1 (e.g., .25).</p>\r
19804  * <p>The basic rules for specifying column widths are pretty simple.  The logic makes two passes through the\r
19805  * set of contained panels.  During the first layout pass, all panels that either have a fixed width or none\r
19806  * specified (auto) are skipped, but their widths are subtracted from the overall container width.  During the second\r
19807  * pass, all panels with columnWidths are assigned pixel widths in proportion to their percentages based on\r
19808  * the total <b>remaining</b> container width.  In other words, percentage width panels are designed to fill the space\r
19809  * left over by all the fixed-width and/or auto-width panels.  Because of this, while you can specify any number of columns\r
19810  * with different percentages, the columnWidths must always add up to 1 (or 100%) when added together, otherwise your\r
19811  * layout may not render as expected.  Example usage:</p>\r
19812  * <pre><code>\r
19813 // All columns are percentages -- they must add up to 1\r
19814 var p = new Ext.Panel({\r
19815     title: 'Column Layout - Percentage Only',\r
19816     layout:'column',\r
19817     items: [{\r
19818         title: 'Column 1',\r
19819         columnWidth: .25\r
19820     },{\r
19821         title: 'Column 2',\r
19822         columnWidth: .6\r
19823     },{\r
19824         title: 'Column 3',\r
19825         columnWidth: .15\r
19826     }]\r
19827 });\r
19828 \r
19829 // Mix of width and columnWidth -- all columnWidth values must add up\r
19830 // to 1. The first column will take up exactly 120px, and the last two\r
19831 // columns will fill the remaining container width.\r
19832 var p = new Ext.Panel({\r
19833     title: 'Column Layout - Mixed',\r
19834     layout:'column',\r
19835     items: [{\r
19836         title: 'Column 1',\r
19837         width: 120\r
19838     },{\r
19839         title: 'Column 2',\r
19840         columnWidth: .8\r
19841     },{\r
19842         title: 'Column 3',\r
19843         columnWidth: .2\r
19844     }]\r
19845 });\r
19846 </code></pre>\r
19847  */\r
19848 Ext.layout.ColumnLayout = Ext.extend(Ext.layout.ContainerLayout, {\r
19849     // private\r
19850     monitorResize:true,\r
19851 \r
19852     type: 'column',\r
19853 \r
19854     extraCls: 'x-column',\r
19855 \r
19856     scrollOffset : 0,\r
19857 \r
19858     // private\r
19859 \r
19860     targetCls: 'x-column-layout-ct',\r
19861 \r
19862     isValidParent : function(c, target){\r
19863         return this.innerCt && c.getPositionEl().dom.parentNode == this.innerCt.dom;\r
19864     },\r
19865 \r
19866     getLayoutTargetSize : function() {\r
19867         var target = this.container.getLayoutTarget(), ret;\r
19868         if (target) {\r
19869             ret = target.getViewSize();\r
19870             ret.width -= target.getPadding('lr');\r
19871             ret.height -= target.getPadding('tb');\r
19872         }\r
19873         return ret;\r
19874     },\r
19875 \r
19876     renderAll : function(ct, target) {\r
19877         if(!this.innerCt){\r
19878             // the innerCt prevents wrapping and shuffling while\r
19879             // the container is resizing\r
19880             this.innerCt = target.createChild({cls:'x-column-inner'});\r
19881             this.innerCt.createChild({cls:'x-clear'});\r
19882         }\r
19883         Ext.layout.ColumnLayout.superclass.renderAll.call(this, ct, this.innerCt);\r
19884     },\r
19885 \r
19886     // private\r
19887     onLayout : function(ct, target){\r
19888         var cs = ct.items.items, len = cs.length, c, i;\r
19889 \r
19890         this.renderAll(ct, target);\r
19891 \r
19892         var size = this.getLayoutTargetSize();\r
19893 \r
19894         if(size.width < 1 && size.height < 1){ // display none?\r
19895             return;\r
19896         }\r
19897 \r
19898         var w = size.width - this.scrollOffset,\r
19899             h = size.height,\r
19900             pw = w;\r
19901 \r
19902         this.innerCt.setWidth(w);\r
19903 \r
19904         // some columns can be percentages while others are fixed\r
19905         // so we need to make 2 passes\r
19906 \r
19907         for(i = 0; i < len; i++){\r
19908             c = cs[i];\r
19909             if(!c.columnWidth){\r
19910                 pw -= (c.getWidth() + c.getPositionEl().getMargins('lr'));\r
19911             }\r
19912         }\r
19913 \r
19914         pw = pw < 0 ? 0 : pw;\r
19915 \r
19916         for(i = 0; i < len; i++){\r
19917             c = cs[i];\r
19918             if(c.columnWidth){\r
19919                 c.setSize(Math.floor(c.columnWidth * pw) - c.getPositionEl().getMargins('lr'));\r
19920             }\r
19921         }\r
19922 \r
19923         // Browsers differ as to when they account for scrollbars.  We need to re-measure to see if the scrollbar\r
19924         // spaces were accounted for properly.  If not, re-layout.\r
19925         if (Ext.isIE) {\r
19926             if (i = target.getStyle('overflow') && i != 'hidden' && !this.adjustmentPass) {\r
19927                 var ts = this.getLayoutTargetSize();\r
19928                 if (ts.width != size.width){\r
19929                     this.adjustmentPass = true;\r
19930                     this.onLayout(ct, target);\r
19931                 }\r
19932             }\r
19933         }\r
19934         delete this.adjustmentPass;\r
19935     }\r
19936 \r
19937     /**\r
19938      * @property activeItem\r
19939      * @hide\r
19940      */\r
19941 });\r
19942 \r
19943 Ext.Container.LAYOUTS['column'] = Ext.layout.ColumnLayout;/**
19944  * @class Ext.layout.BorderLayout
19945  * @extends Ext.layout.ContainerLayout
19946  * <p>This is a multi-pane, application-oriented UI layout style that supports multiple
19947  * nested panels, automatic {@link Ext.layout.BorderLayout.Region#split split} bars between
19948  * {@link Ext.layout.BorderLayout.Region#BorderLayout.Region regions} and built-in
19949  * {@link Ext.layout.BorderLayout.Region#collapsible expanding and collapsing} of regions.</p>
19950  * <p>This class is intended to be extended or created via the <tt>layout:'border'</tt>
19951  * {@link Ext.Container#layout} config, and should generally not need to be created directly
19952  * via the new keyword.</p>
19953  * <p>BorderLayout does not have any direct config options (other than inherited ones).
19954  * All configuration options available for customizing the BorderLayout are at the
19955  * {@link Ext.layout.BorderLayout.Region} and {@link Ext.layout.BorderLayout.SplitRegion}
19956  * levels.</p>
19957  * <p>Example usage:</p>
19958  * <pre><code>
19959 var myBorderPanel = new Ext.Panel({
19960     {@link Ext.Component#renderTo renderTo}: document.body,
19961     {@link Ext.BoxComponent#width width}: 700,
19962     {@link Ext.BoxComponent#height height}: 500,
19963     {@link Ext.Panel#title title}: 'Border Layout',
19964     {@link Ext.Container#layout layout}: 'border',
19965     {@link Ext.Container#items items}: [{
19966         {@link Ext.Panel#title title}: 'South Region is resizable',
19967         {@link Ext.layout.BorderLayout.Region#BorderLayout.Region region}: 'south',     // position for region
19968         {@link Ext.BoxComponent#height height}: 100,
19969         {@link Ext.layout.BorderLayout.Region#split split}: true,         // enable resizing
19970         {@link Ext.SplitBar#minSize minSize}: 75,         // defaults to {@link Ext.layout.BorderLayout.Region#minHeight 50}
19971         {@link Ext.SplitBar#maxSize maxSize}: 150,
19972         {@link Ext.layout.BorderLayout.Region#margins margins}: '0 5 5 5'
19973     },{
19974         // xtype: 'panel' implied by default
19975         {@link Ext.Panel#title title}: 'West Region is collapsible',
19976         {@link Ext.layout.BorderLayout.Region#BorderLayout.Region region}:'west',
19977         {@link Ext.layout.BorderLayout.Region#margins margins}: '5 0 0 5',
19978         {@link Ext.BoxComponent#width width}: 200,
19979         {@link Ext.layout.BorderLayout.Region#collapsible collapsible}: true,   // make collapsible
19980         {@link Ext.layout.BorderLayout.Region#cmargins cmargins}: '5 5 0 5', // adjust top margin when collapsed
19981         {@link Ext.Component#id id}: 'west-region-container',
19982         {@link Ext.Container#layout layout}: 'fit',
19983         {@link Ext.Panel#unstyled unstyled}: true
19984     },{
19985         {@link Ext.Panel#title title}: 'Center Region',
19986         {@link Ext.layout.BorderLayout.Region#BorderLayout.Region region}: 'center',     // center region is required, no width/height specified
19987         {@link Ext.Component#xtype xtype}: 'container',
19988         {@link Ext.Container#layout layout}: 'fit',
19989         {@link Ext.layout.BorderLayout.Region#margins margins}: '5 5 0 0'
19990     }]
19991 });
19992 </code></pre>
19993  * <p><b><u>Notes</u></b>:</p><div class="mdetail-params"><ul>
19994  * <li>Any container using the BorderLayout <b>must</b> have a child item with <tt>region:'center'</tt>.
19995  * The child item in the center region will always be resized to fill the remaining space not used by
19996  * the other regions in the layout.</li>
19997  * <li>Any child items with a region of <tt>west</tt> or <tt>east</tt> must have <tt>width</tt> defined
19998  * (an integer representing the number of pixels that the region should take up).</li>
19999  * <li>Any child items with a region of <tt>north</tt> or <tt>south</tt> must have <tt>height</tt> defined.</li>
20000  * <li>The regions of a BorderLayout are <b>fixed at render time</b> and thereafter, its child Components may not be removed or added</b>.  To add/remove
20001  * Components within a BorderLayout, have them wrapped by an additional Container which is directly
20002  * managed by the BorderLayout.  If the region is to be collapsible, the Container used directly
20003  * by the BorderLayout manager should be a Panel.  In the following example a Container (an Ext.Panel)
20004  * is added to the west region:
20005  * <div style="margin-left:16px"><pre><code>
20006 wrc = {@link Ext#getCmp Ext.getCmp}('west-region-container');
20007 wrc.{@link Ext.Panel#removeAll removeAll}();
20008 wrc.{@link Ext.Container#add add}({
20009     title: 'Added Panel',
20010     html: 'Some content'
20011 });
20012 wrc.{@link Ext.Container#doLayout doLayout}();
20013  * </code></pre></div>
20014  * </li>
20015  * <li> To reference a {@link Ext.layout.BorderLayout.Region Region}:
20016  * <div style="margin-left:16px"><pre><code>
20017 wr = myBorderPanel.layout.west;
20018  * </code></pre></div>
20019  * </li>
20020  * </ul></div>
20021  */
20022 Ext.layout.BorderLayout = Ext.extend(Ext.layout.ContainerLayout, {
20023     // private
20024     monitorResize:true,
20025     // private
20026     rendered : false,
20027
20028     type: 'border',
20029
20030     targetCls: 'x-border-layout-ct',
20031
20032     getLayoutTargetSize : function() {
20033         var target = this.container.getLayoutTarget();
20034         return target ? target.getViewSize() : {};
20035     },
20036
20037     // private
20038     onLayout : function(ct, target){
20039         var collapsed, i, c, pos, items = ct.items.items, len = items.length;
20040         if(!this.rendered){
20041             collapsed = [];
20042             for(i = 0; i < len; i++) {
20043                 c = items[i];
20044                 pos = c.region;
20045                 if(c.collapsed){
20046                     collapsed.push(c);
20047                 }
20048                 c.collapsed = false;
20049                 if(!c.rendered){
20050                     c.render(target, i);
20051                     c.getPositionEl().addClass('x-border-panel');
20052                 }
20053                 this[pos] = pos != 'center' && c.split ?
20054                     new Ext.layout.BorderLayout.SplitRegion(this, c.initialConfig, pos) :
20055                     new Ext.layout.BorderLayout.Region(this, c.initialConfig, pos);
20056                 this[pos].render(target, c);
20057             }
20058             this.rendered = true;
20059         }
20060
20061         var size = this.getLayoutTargetSize();
20062         if(size.width < 20 || size.height < 20){ // display none?
20063             if(collapsed){
20064                 this.restoreCollapsed = collapsed;
20065             }
20066             return;
20067         }else if(this.restoreCollapsed){
20068             collapsed = this.restoreCollapsed;
20069             delete this.restoreCollapsed;
20070         }
20071
20072         var w = size.width, h = size.height,
20073             centerW = w, centerH = h, centerY = 0, centerX = 0,
20074             n = this.north, s = this.south, west = this.west, e = this.east, c = this.center,
20075             b, m, totalWidth, totalHeight;
20076         if(!c && Ext.layout.BorderLayout.WARN !== false){
20077             throw 'No center region defined in BorderLayout ' + ct.id;
20078         }
20079
20080         if(n && n.isVisible()){
20081             b = n.getSize();
20082             m = n.getMargins();
20083             b.width = w - (m.left+m.right);
20084             b.x = m.left;
20085             b.y = m.top;
20086             centerY = b.height + b.y + m.bottom;
20087             centerH -= centerY;
20088             n.applyLayout(b);
20089         }
20090         if(s && s.isVisible()){
20091             b = s.getSize();
20092             m = s.getMargins();
20093             b.width = w - (m.left+m.right);
20094             b.x = m.left;
20095             totalHeight = (b.height + m.top + m.bottom);
20096             b.y = h - totalHeight + m.top;
20097             centerH -= totalHeight;
20098             s.applyLayout(b);
20099         }
20100         if(west && west.isVisible()){
20101             b = west.getSize();
20102             m = west.getMargins();
20103             b.height = centerH - (m.top+m.bottom);
20104             b.x = m.left;
20105             b.y = centerY + m.top;
20106             totalWidth = (b.width + m.left + m.right);
20107             centerX += totalWidth;
20108             centerW -= totalWidth;
20109             west.applyLayout(b);
20110         }
20111         if(e && e.isVisible()){
20112             b = e.getSize();
20113             m = e.getMargins();
20114             b.height = centerH - (m.top+m.bottom);
20115             totalWidth = (b.width + m.left + m.right);
20116             b.x = w - totalWidth + m.left;
20117             b.y = centerY + m.top;
20118             centerW -= totalWidth;
20119             e.applyLayout(b);
20120         }
20121         if(c){
20122             m = c.getMargins();
20123             var centerBox = {
20124                 x: centerX + m.left,
20125                 y: centerY + m.top,
20126                 width: centerW - (m.left+m.right),
20127                 height: centerH - (m.top+m.bottom)
20128             };
20129             c.applyLayout(centerBox);
20130         }
20131         if(collapsed){
20132             for(i = 0, len = collapsed.length; i < len; i++){
20133                 collapsed[i].collapse(false);
20134             }
20135         }
20136         if(Ext.isIE && Ext.isStrict){ // workaround IE strict repainting issue
20137             target.repaint();
20138         }
20139         // Putting a border layout into an overflowed container is NOT correct and will make a second layout pass necessary.
20140         if (i = target.getStyle('overflow') && i != 'hidden' && !this.adjustmentPass) {
20141             var ts = this.getLayoutTargetSize();
20142             if (ts.width != size.width || ts.height != size.height){
20143                 this.adjustmentPass = true;
20144                 this.onLayout(ct, target);
20145             }
20146         }
20147         delete this.adjustmentPass;
20148     },
20149
20150     destroy: function() {
20151         var r = ['north', 'south', 'east', 'west'], i, region;
20152         for (i = 0; i < r.length; i++) {
20153             region = this[r[i]];
20154             if(region){
20155                 if(region.destroy){
20156                     region.destroy();
20157                 }else if (region.split){
20158                     region.split.destroy(true);
20159                 }
20160             }
20161         }
20162         Ext.layout.BorderLayout.superclass.destroy.call(this);
20163     }
20164
20165     /**
20166      * @property activeItem
20167      * @hide
20168      */
20169 });
20170
20171 /**
20172  * @class Ext.layout.BorderLayout.Region
20173  * <p>This is a region of a {@link Ext.layout.BorderLayout BorderLayout} that acts as a subcontainer
20174  * within the layout.  Each region has its own {@link Ext.layout.ContainerLayout layout} that is
20175  * independent of other regions and the containing BorderLayout, and can be any of the
20176  * {@link Ext.layout.ContainerLayout valid Ext layout types}.</p>
20177  * <p>Region size is managed automatically and cannot be changed by the user -- for
20178  * {@link #split resizable regions}, see {@link Ext.layout.BorderLayout.SplitRegion}.</p>
20179  * @constructor
20180  * Create a new Region.
20181  * @param {Layout} layout The {@link Ext.layout.BorderLayout BorderLayout} instance that is managing this Region.
20182  * @param {Object} config The configuration options
20183  * @param {String} position The region position.  Valid values are: <tt>north</tt>, <tt>south</tt>,
20184  * <tt>east</tt>, <tt>west</tt> and <tt>center</tt>.  Every {@link Ext.layout.BorderLayout BorderLayout}
20185  * <b>must have a center region</b> for the primary content -- all other regions are optional.
20186  */
20187 Ext.layout.BorderLayout.Region = function(layout, config, pos){
20188     Ext.apply(this, config);
20189     this.layout = layout;
20190     this.position = pos;
20191     this.state = {};
20192     if(typeof this.margins == 'string'){
20193         this.margins = this.layout.parseMargins(this.margins);
20194     }
20195     this.margins = Ext.applyIf(this.margins || {}, this.defaultMargins);
20196     if(this.collapsible){
20197         if(typeof this.cmargins == 'string'){
20198             this.cmargins = this.layout.parseMargins(this.cmargins);
20199         }
20200         if(this.collapseMode == 'mini' && !this.cmargins){
20201             this.cmargins = {left:0,top:0,right:0,bottom:0};
20202         }else{
20203             this.cmargins = Ext.applyIf(this.cmargins || {},
20204                 pos == 'north' || pos == 'south' ? this.defaultNSCMargins : this.defaultEWCMargins);
20205         }
20206     }
20207 };
20208
20209 Ext.layout.BorderLayout.Region.prototype = {
20210     /**
20211      * @cfg {Boolean} animFloat
20212      * When a collapsed region's bar is clicked, the region's panel will be displayed as a floated
20213      * panel that will close again once the user mouses out of that panel (or clicks out if
20214      * <tt>{@link #autoHide} = false</tt>).  Setting <tt>{@link #animFloat} = false</tt> will
20215      * prevent the open and close of these floated panels from being animated (defaults to <tt>true</tt>).
20216      */
20217     /**
20218      * @cfg {Boolean} autoHide
20219      * When a collapsed region's bar is clicked, the region's panel will be displayed as a floated
20220      * panel.  If <tt>autoHide = true</tt>, the panel will automatically hide after the user mouses
20221      * out of the panel.  If <tt>autoHide = false</tt>, the panel will continue to display until the
20222      * user clicks outside of the panel (defaults to <tt>true</tt>).
20223      */
20224     /**
20225      * @cfg {String} collapseMode
20226      * <tt>collapseMode</tt> supports two configuration values:<div class="mdetail-params"><ul>
20227      * <li><b><tt>undefined</tt></b> (default)<div class="sub-desc">By default, {@link #collapsible}
20228      * regions are collapsed by clicking the expand/collapse tool button that renders into the region's
20229      * title bar.</div></li>
20230      * <li><b><tt>'mini'</tt></b><div class="sub-desc">Optionally, when <tt>collapseMode</tt> is set to
20231      * <tt>'mini'</tt> the region's split bar will also display a small collapse button in the center of
20232      * the bar. In <tt>'mini'</tt> mode the region will collapse to a thinner bar than in normal mode.
20233      * </div></li>
20234      * </ul></div></p>
20235      * <p><b>Note</b>: if a collapsible region does not have a title bar, then set <tt>collapseMode =
20236      * 'mini'</tt> and <tt>{@link #split} = true</tt> in order for the region to be {@link #collapsible}
20237      * by the user as the expand/collapse tool button (that would go in the title bar) will not be rendered.</p>
20238      * <p>See also <tt>{@link #cmargins}</tt>.</p>
20239      */
20240     /**
20241      * @cfg {Object} margins
20242      * An object containing margins to apply to the region when in the expanded state in the
20243      * format:<pre><code>
20244 {
20245     top: (top margin),
20246     right: (right margin),
20247     bottom: (bottom margin),
20248     left: (left margin)
20249 }</code></pre>
20250      * <p>May also be a string containing space-separated, numeric margin values. The order of the
20251      * sides associated with each value matches the way CSS processes margin values:</p>
20252      * <p><div class="mdetail-params"><ul>
20253      * <li>If there is only one value, it applies to all sides.</li>
20254      * <li>If there are two values, the top and bottom borders are set to the first value and the
20255      * right and left are set to the second.</li>
20256      * <li>If there are three values, the top is set to the first value, the left and right are set
20257      * to the second, and the bottom is set to the third.</li>
20258      * <li>If there are four values, they apply to the top, right, bottom, and left, respectively.</li>
20259      * </ul></div></p>
20260      * <p>Defaults to:</p><pre><code>
20261      * {top:0, right:0, bottom:0, left:0}
20262      * </code></pre>
20263      */
20264     /**
20265      * @cfg {Object} cmargins
20266      * An object containing margins to apply to the region when in the collapsed state in the
20267      * format:<pre><code>
20268 {
20269     top: (top margin),
20270     right: (right margin),
20271     bottom: (bottom margin),
20272     left: (left margin)
20273 }</code></pre>
20274      * <p>May also be a string containing space-separated, numeric margin values. The order of the
20275      * sides associated with each value matches the way CSS processes margin values.</p>
20276      * <p><ul>
20277      * <li>If there is only one value, it applies to all sides.</li>
20278      * <li>If there are two values, the top and bottom borders are set to the first value and the
20279      * right and left are set to the second.</li>
20280      * <li>If there are three values, the top is set to the first value, the left and right are set
20281      * to the second, and the bottom is set to the third.</li>
20282      * <li>If there are four values, they apply to the top, right, bottom, and left, respectively.</li>
20283      * </ul></p>
20284      */
20285     /**
20286      * @cfg {Boolean} collapsible
20287      * <p><tt>true</tt> to allow the user to collapse this region (defaults to <tt>false</tt>).  If
20288      * <tt>true</tt>, an expand/collapse tool button will automatically be rendered into the title
20289      * bar of the region, otherwise the button will not be shown.</p>
20290      * <p><b>Note</b>: that a title bar is required to display the collapse/expand toggle button -- if
20291      * no <tt>title</tt> is specified for the region's panel, the region will only be collapsible if
20292      * <tt>{@link #collapseMode} = 'mini'</tt> and <tt>{@link #split} = true</tt>.
20293      */
20294     collapsible : false,
20295     /**
20296      * @cfg {Boolean} split
20297      * <p><tt>true</tt> to create a {@link Ext.layout.BorderLayout.SplitRegion SplitRegion} and
20298      * display a 5px wide {@link Ext.SplitBar} between this region and its neighbor, allowing the user to
20299      * resize the regions dynamically.  Defaults to <tt>false</tt> creating a
20300      * {@link Ext.layout.BorderLayout.Region Region}.</p><br>
20301      * <p><b>Notes</b>:</p><div class="mdetail-params"><ul>
20302      * <li>this configuration option is ignored if <tt>region='center'</tt></li>
20303      * <li>when <tt>split == true</tt>, it is common to specify a
20304      * <tt>{@link Ext.SplitBar#minSize minSize}</tt> and <tt>{@link Ext.SplitBar#maxSize maxSize}</tt>
20305      * for the {@link Ext.BoxComponent BoxComponent} representing the region. These are not native
20306      * configs of {@link Ext.BoxComponent BoxComponent}, and are used only by this class.</li>
20307      * <li>if <tt>{@link #collapseMode} = 'mini'</tt> requires <tt>split = true</tt> to reserve space
20308      * for the collapse tool</tt></li>
20309      * </ul></div>
20310      */
20311     split:false,
20312     /**
20313      * @cfg {Boolean} floatable
20314      * <tt>true</tt> to allow clicking a collapsed region's bar to display the region's panel floated
20315      * above the layout, <tt>false</tt> to force the user to fully expand a collapsed region by
20316      * clicking the expand button to see it again (defaults to <tt>true</tt>).
20317      */
20318     floatable: true,
20319     /**
20320      * @cfg {Number} minWidth
20321      * <p>The minimum allowable width in pixels for this region (defaults to <tt>50</tt>).
20322      * <tt>maxWidth</tt> may also be specified.</p><br>
20323      * <p><b>Note</b>: setting the <tt>{@link Ext.SplitBar#minSize minSize}</tt> /
20324      * <tt>{@link Ext.SplitBar#maxSize maxSize}</tt> supersedes any specified
20325      * <tt>minWidth</tt> / <tt>maxWidth</tt>.</p>
20326      */
20327     minWidth:50,
20328     /**
20329      * @cfg {Number} minHeight
20330      * The minimum allowable height in pixels for this region (defaults to <tt>50</tt>)
20331      * <tt>maxHeight</tt> may also be specified.</p><br>
20332      * <p><b>Note</b>: setting the <tt>{@link Ext.SplitBar#minSize minSize}</tt> /
20333      * <tt>{@link Ext.SplitBar#maxSize maxSize}</tt> supersedes any specified
20334      * <tt>minHeight</tt> / <tt>maxHeight</tt>.</p>
20335      */
20336     minHeight:50,
20337
20338     // private
20339     defaultMargins : {left:0,top:0,right:0,bottom:0},
20340     // private
20341     defaultNSCMargins : {left:5,top:5,right:5,bottom:5},
20342     // private
20343     defaultEWCMargins : {left:5,top:0,right:5,bottom:0},
20344     floatingZIndex: 100,
20345
20346     /**
20347      * True if this region is collapsed. Read-only.
20348      * @type Boolean
20349      * @property
20350      */
20351     isCollapsed : false,
20352
20353     /**
20354      * This region's panel.  Read-only.
20355      * @type Ext.Panel
20356      * @property panel
20357      */
20358     /**
20359      * This region's layout.  Read-only.
20360      * @type Layout
20361      * @property layout
20362      */
20363     /**
20364      * This region's layout position (north, south, east, west or center).  Read-only.
20365      * @type String
20366      * @property position
20367      */
20368
20369     // private
20370     render : function(ct, p){
20371         this.panel = p;
20372         p.el.enableDisplayMode();
20373         this.targetEl = ct;
20374         this.el = p.el;
20375
20376         var gs = p.getState, ps = this.position;
20377         p.getState = function(){
20378             return Ext.apply(gs.call(p) || {}, this.state);
20379         }.createDelegate(this);
20380
20381         if(ps != 'center'){
20382             p.allowQueuedExpand = false;
20383             p.on({
20384                 beforecollapse: this.beforeCollapse,
20385                 collapse: this.onCollapse,
20386                 beforeexpand: this.beforeExpand,
20387                 expand: this.onExpand,
20388                 hide: this.onHide,
20389                 show: this.onShow,
20390                 scope: this
20391             });
20392             if(this.collapsible || this.floatable){
20393                 p.collapseEl = 'el';
20394                 p.slideAnchor = this.getSlideAnchor();
20395             }
20396             if(p.tools && p.tools.toggle){
20397                 p.tools.toggle.addClass('x-tool-collapse-'+ps);
20398                 p.tools.toggle.addClassOnOver('x-tool-collapse-'+ps+'-over');
20399             }
20400         }
20401     },
20402
20403     // private
20404     getCollapsedEl : function(){
20405         if(!this.collapsedEl){
20406             if(!this.toolTemplate){
20407                 var tt = new Ext.Template(
20408                      '<div class="x-tool x-tool-{id}">&#160;</div>'
20409                 );
20410                 tt.disableFormats = true;
20411                 tt.compile();
20412                 Ext.layout.BorderLayout.Region.prototype.toolTemplate = tt;
20413             }
20414             this.collapsedEl = this.targetEl.createChild({
20415                 cls: "x-layout-collapsed x-layout-collapsed-"+this.position,
20416                 id: this.panel.id + '-xcollapsed'
20417             });
20418             this.collapsedEl.enableDisplayMode('block');
20419
20420             if(this.collapseMode == 'mini'){
20421                 this.collapsedEl.addClass('x-layout-cmini-'+this.position);
20422                 this.miniCollapsedEl = this.collapsedEl.createChild({
20423                     cls: "x-layout-mini x-layout-mini-"+this.position, html: "&#160;"
20424                 });
20425                 this.miniCollapsedEl.addClassOnOver('x-layout-mini-over');
20426                 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
20427                 this.collapsedEl.on('click', this.onExpandClick, this, {stopEvent:true});
20428             }else {
20429                 if(this.collapsible !== false && !this.hideCollapseTool) {
20430                     var t = this.toolTemplate.append(
20431                             this.collapsedEl.dom,
20432                             {id:'expand-'+this.position}, true);
20433                     t.addClassOnOver('x-tool-expand-'+this.position+'-over');
20434                     t.on('click', this.onExpandClick, this, {stopEvent:true});
20435                 }
20436                 if(this.floatable !== false || this.titleCollapse){
20437                    this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
20438                    this.collapsedEl.on("click", this[this.floatable ? 'collapseClick' : 'onExpandClick'], this);
20439                 }
20440             }
20441         }
20442         return this.collapsedEl;
20443     },
20444
20445     // private
20446     onExpandClick : function(e){
20447         if(this.isSlid){
20448             this.panel.expand(false);
20449         }else{
20450             this.panel.expand();
20451         }
20452     },
20453
20454     // private
20455     onCollapseClick : function(e){
20456         this.panel.collapse();
20457     },
20458
20459     // private
20460     beforeCollapse : function(p, animate){
20461         this.lastAnim = animate;
20462         if(this.splitEl){
20463             this.splitEl.hide();
20464         }
20465         this.getCollapsedEl().show();
20466         var el = this.panel.getEl();
20467         this.originalZIndex = el.getStyle('z-index');
20468         el.setStyle('z-index', 100);
20469         this.isCollapsed = true;
20470         this.layout.layout();
20471     },
20472
20473     // private
20474     onCollapse : function(animate){
20475         this.panel.el.setStyle('z-index', 1);
20476         if(this.lastAnim === false || this.panel.animCollapse === false){
20477             this.getCollapsedEl().dom.style.visibility = 'visible';
20478         }else{
20479             this.getCollapsedEl().slideIn(this.panel.slideAnchor, {duration:.2});
20480         }
20481         this.state.collapsed = true;
20482         this.panel.saveState();
20483     },
20484
20485     // private
20486     beforeExpand : function(animate){
20487         if(this.isSlid){
20488             this.afterSlideIn();
20489         }
20490         var c = this.getCollapsedEl();
20491         this.el.show();
20492         if(this.position == 'east' || this.position == 'west'){
20493             this.panel.setSize(undefined, c.getHeight());
20494         }else{
20495             this.panel.setSize(c.getWidth(), undefined);
20496         }
20497         c.hide();
20498         c.dom.style.visibility = 'hidden';
20499         this.panel.el.setStyle('z-index', this.floatingZIndex);
20500     },
20501
20502     // private
20503     onExpand : function(){
20504         this.isCollapsed = false;
20505         if(this.splitEl){
20506             this.splitEl.show();
20507         }
20508         this.layout.layout();
20509         this.panel.el.setStyle('z-index', this.originalZIndex);
20510         this.state.collapsed = false;
20511         this.panel.saveState();
20512     },
20513
20514     // private
20515     collapseClick : function(e){
20516         if(this.isSlid){
20517            e.stopPropagation();
20518            this.slideIn();
20519         }else{
20520            e.stopPropagation();
20521            this.slideOut();
20522         }
20523     },
20524
20525     // private
20526     onHide : function(){
20527         if(this.isCollapsed){
20528             this.getCollapsedEl().hide();
20529         }else if(this.splitEl){
20530             this.splitEl.hide();
20531         }
20532     },
20533
20534     // private
20535     onShow : function(){
20536         if(this.isCollapsed){
20537             this.getCollapsedEl().show();
20538         }else if(this.splitEl){
20539             this.splitEl.show();
20540         }
20541     },
20542
20543     /**
20544      * True if this region is currently visible, else false.
20545      * @return {Boolean}
20546      */
20547     isVisible : function(){
20548         return !this.panel.hidden;
20549     },
20550
20551     /**
20552      * Returns the current margins for this region.  If the region is collapsed, the
20553      * {@link #cmargins} (collapsed margins) value will be returned, otherwise the
20554      * {@link #margins} value will be returned.
20555      * @return {Object} An object containing the element's margins: <tt>{left: (left
20556      * margin), top: (top margin), right: (right margin), bottom: (bottom margin)}</tt>
20557      */
20558     getMargins : function(){
20559         return this.isCollapsed && this.cmargins ? this.cmargins : this.margins;
20560     },
20561
20562     /**
20563      * Returns the current size of this region.  If the region is collapsed, the size of the
20564      * collapsedEl will be returned, otherwise the size of the region's panel will be returned.
20565      * @return {Object} An object containing the element's size: <tt>{width: (element width),
20566      * height: (element height)}</tt>
20567      */
20568     getSize : function(){
20569         return this.isCollapsed ? this.getCollapsedEl().getSize() : this.panel.getSize();
20570     },
20571
20572     /**
20573      * Sets the specified panel as the container element for this region.
20574      * @param {Ext.Panel} panel The new panel
20575      */
20576     setPanel : function(panel){
20577         this.panel = panel;
20578     },
20579
20580     /**
20581      * Returns the minimum allowable width for this region.
20582      * @return {Number} The minimum width
20583      */
20584     getMinWidth: function(){
20585         return this.minWidth;
20586     },
20587
20588     /**
20589      * Returns the minimum allowable height for this region.
20590      * @return {Number} The minimum height
20591      */
20592     getMinHeight: function(){
20593         return this.minHeight;
20594     },
20595
20596     // private
20597     applyLayoutCollapsed : function(box){
20598         var ce = this.getCollapsedEl();
20599         ce.setLeftTop(box.x, box.y);
20600         ce.setSize(box.width, box.height);
20601     },
20602
20603     // private
20604     applyLayout : function(box){
20605         if(this.isCollapsed){
20606             this.applyLayoutCollapsed(box);
20607         }else{
20608             this.panel.setPosition(box.x, box.y);
20609             this.panel.setSize(box.width, box.height);
20610         }
20611     },
20612
20613     // private
20614     beforeSlide: function(){
20615         this.panel.beforeEffect();
20616     },
20617
20618     // private
20619     afterSlide : function(){
20620         this.panel.afterEffect();
20621     },
20622
20623     // private
20624     initAutoHide : function(){
20625         if(this.autoHide !== false){
20626             if(!this.autoHideHd){
20627                 var st = new Ext.util.DelayedTask(this.slideIn, this);
20628                 this.autoHideHd = {
20629                     "mouseout": function(e){
20630                         if(!e.within(this.el, true)){
20631                             st.delay(500);
20632                         }
20633                     },
20634                     "mouseover" : function(e){
20635                         st.cancel();
20636                     },
20637                     scope : this
20638                 };
20639             }
20640             this.el.on(this.autoHideHd);
20641             this.collapsedEl.on(this.autoHideHd);
20642         }
20643     },
20644
20645     // private
20646     clearAutoHide : function(){
20647         if(this.autoHide !== false){
20648             this.el.un("mouseout", this.autoHideHd.mouseout);
20649             this.el.un("mouseover", this.autoHideHd.mouseover);
20650             this.collapsedEl.un("mouseout", this.autoHideHd.mouseout);
20651             this.collapsedEl.un("mouseover", this.autoHideHd.mouseover);
20652         }
20653     },
20654
20655     // private
20656     clearMonitor : function(){
20657         Ext.getDoc().un("click", this.slideInIf, this);
20658     },
20659
20660     /**
20661      * If this Region is {@link #floatable}, this method slides this Region into full visibility <i>over the top
20662      * of the center Region</i> where it floats until either {@link #slideIn} is called, or other regions of the layout
20663      * are clicked, or the mouse exits the Region.
20664      */
20665     slideOut : function(){
20666         if(this.isSlid || this.el.hasActiveFx()){
20667             return;
20668         }
20669         this.isSlid = true;
20670         var ts = this.panel.tools, dh, pc;
20671         if(ts && ts.toggle){
20672             ts.toggle.hide();
20673         }
20674         this.el.show();
20675
20676         // Temporarily clear the collapsed flag so we can onResize the panel on the slide
20677         pc = this.panel.collapsed;
20678         this.panel.collapsed = false;
20679
20680         if(this.position == 'east' || this.position == 'west'){
20681             // Temporarily clear the deferHeight flag so we can size the height on the slide
20682             dh = this.panel.deferHeight;
20683             this.panel.deferHeight = false;
20684
20685             this.panel.setSize(undefined, this.collapsedEl.getHeight());
20686
20687             // Put the deferHeight flag back after setSize
20688             this.panel.deferHeight = dh;
20689         }else{
20690             this.panel.setSize(this.collapsedEl.getWidth(), undefined);
20691         }
20692
20693         // Put the collapsed flag back after onResize
20694         this.panel.collapsed = pc;
20695
20696         this.restoreLT = [this.el.dom.style.left, this.el.dom.style.top];
20697         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
20698         this.el.setStyle("z-index", this.floatingZIndex+2);
20699         this.panel.el.replaceClass('x-panel-collapsed', 'x-panel-floating');
20700         if(this.animFloat !== false){
20701             this.beforeSlide();
20702             this.el.slideIn(this.getSlideAnchor(), {
20703                 callback: function(){
20704                     this.afterSlide();
20705                     this.initAutoHide();
20706                     Ext.getDoc().on("click", this.slideInIf, this);
20707                 },
20708                 scope: this,
20709                 block: true
20710             });
20711         }else{
20712             this.initAutoHide();
20713              Ext.getDoc().on("click", this.slideInIf, this);
20714         }
20715     },
20716
20717     // private
20718     afterSlideIn : function(){
20719         this.clearAutoHide();
20720         this.isSlid = false;
20721         this.clearMonitor();
20722         this.el.setStyle("z-index", "");
20723         this.panel.el.replaceClass('x-panel-floating', 'x-panel-collapsed');
20724         this.el.dom.style.left = this.restoreLT[0];
20725         this.el.dom.style.top = this.restoreLT[1];
20726
20727         var ts = this.panel.tools;
20728         if(ts && ts.toggle){
20729             ts.toggle.show();
20730         }
20731     },
20732
20733     /**
20734      * If this Region is {@link #floatable}, and this Region has been slid into floating visibility, then this method slides
20735      * this region back into its collapsed state.
20736      */
20737     slideIn : function(cb){
20738         if(!this.isSlid || this.el.hasActiveFx()){
20739             Ext.callback(cb);
20740             return;
20741         }
20742         this.isSlid = false;
20743         if(this.animFloat !== false){
20744             this.beforeSlide();
20745             this.el.slideOut(this.getSlideAnchor(), {
20746                 callback: function(){
20747                     this.el.hide();
20748                     this.afterSlide();
20749                     this.afterSlideIn();
20750                     Ext.callback(cb);
20751                 },
20752                 scope: this,
20753                 block: true
20754             });
20755         }else{
20756             this.el.hide();
20757             this.afterSlideIn();
20758         }
20759     },
20760
20761     // private
20762     slideInIf : function(e){
20763         if(!e.within(this.el)){
20764             this.slideIn();
20765         }
20766     },
20767
20768     // private
20769     anchors : {
20770         "west" : "left",
20771         "east" : "right",
20772         "north" : "top",
20773         "south" : "bottom"
20774     },
20775
20776     // private
20777     sanchors : {
20778         "west" : "l",
20779         "east" : "r",
20780         "north" : "t",
20781         "south" : "b"
20782     },
20783
20784     // private
20785     canchors : {
20786         "west" : "tl-tr",
20787         "east" : "tr-tl",
20788         "north" : "tl-bl",
20789         "south" : "bl-tl"
20790     },
20791
20792     // private
20793     getAnchor : function(){
20794         return this.anchors[this.position];
20795     },
20796
20797     // private
20798     getCollapseAnchor : function(){
20799         return this.canchors[this.position];
20800     },
20801
20802     // private
20803     getSlideAnchor : function(){
20804         return this.sanchors[this.position];
20805     },
20806
20807     // private
20808     getAlignAdj : function(){
20809         var cm = this.cmargins;
20810         switch(this.position){
20811             case "west":
20812                 return [0, 0];
20813             break;
20814             case "east":
20815                 return [0, 0];
20816             break;
20817             case "north":
20818                 return [0, 0];
20819             break;
20820             case "south":
20821                 return [0, 0];
20822             break;
20823         }
20824     },
20825
20826     // private
20827     getExpandAdj : function(){
20828         var c = this.collapsedEl, cm = this.cmargins;
20829         switch(this.position){
20830             case "west":
20831                 return [-(cm.right+c.getWidth()+cm.left), 0];
20832             break;
20833             case "east":
20834                 return [cm.right+c.getWidth()+cm.left, 0];
20835             break;
20836             case "north":
20837                 return [0, -(cm.top+cm.bottom+c.getHeight())];
20838             break;
20839             case "south":
20840                 return [0, cm.top+cm.bottom+c.getHeight()];
20841             break;
20842         }
20843     },
20844
20845     destroy : function(){
20846         Ext.destroy(this.miniCollapsedEl, this.collapsedEl);
20847     }
20848 };
20849
20850 /**
20851  * @class Ext.layout.BorderLayout.SplitRegion
20852  * @extends Ext.layout.BorderLayout.Region
20853  * <p>This is a specialized type of {@link Ext.layout.BorderLayout.Region BorderLayout region} that
20854  * has a built-in {@link Ext.SplitBar} for user resizing of regions.  The movement of the split bar
20855  * is configurable to move either {@link #tickSize smooth or incrementally}.</p>
20856  * @constructor
20857  * Create a new SplitRegion.
20858  * @param {Layout} layout The {@link Ext.layout.BorderLayout BorderLayout} instance that is managing this Region.
20859  * @param {Object} config The configuration options
20860  * @param {String} position The region position.  Valid values are: north, south, east, west and center.  Every
20861  * BorderLayout must have a center region for the primary content -- all other regions are optional.
20862  */
20863 Ext.layout.BorderLayout.SplitRegion = function(layout, config, pos){
20864     Ext.layout.BorderLayout.SplitRegion.superclass.constructor.call(this, layout, config, pos);
20865     // prevent switch
20866     this.applyLayout = this.applyFns[pos];
20867 };
20868
20869 Ext.extend(Ext.layout.BorderLayout.SplitRegion, Ext.layout.BorderLayout.Region, {
20870     /**
20871      * @cfg {Number} tickSize
20872      * The increment, in pixels by which to move this Region's {@link Ext.SplitBar SplitBar}.
20873      * By default, the {@link Ext.SplitBar SplitBar} moves smoothly.
20874      */
20875     /**
20876      * @cfg {String} splitTip
20877      * The tooltip to display when the user hovers over a
20878      * {@link Ext.layout.BorderLayout.Region#collapsible non-collapsible} region's split bar
20879      * (defaults to <tt>"Drag to resize."</tt>).  Only applies if
20880      * <tt>{@link #useSplitTips} = true</tt>.
20881      */
20882     splitTip : "Drag to resize.",
20883     /**
20884      * @cfg {String} collapsibleSplitTip
20885      * The tooltip to display when the user hovers over a
20886      * {@link Ext.layout.BorderLayout.Region#collapsible collapsible} region's split bar
20887      * (defaults to "Drag to resize. Double click to hide."). Only applies if
20888      * <tt>{@link #useSplitTips} = true</tt>.
20889      */
20890     collapsibleSplitTip : "Drag to resize. Double click to hide.",
20891     /**
20892      * @cfg {Boolean} useSplitTips
20893      * <tt>true</tt> to display a tooltip when the user hovers over a region's split bar
20894      * (defaults to <tt>false</tt>).  The tooltip text will be the value of either
20895      * <tt>{@link #splitTip}</tt> or <tt>{@link #collapsibleSplitTip}</tt> as appropriate.
20896      */
20897     useSplitTips : false,
20898
20899     // private
20900     splitSettings : {
20901         north : {
20902             orientation: Ext.SplitBar.VERTICAL,
20903             placement: Ext.SplitBar.TOP,
20904             maxFn : 'getVMaxSize',
20905             minProp: 'minHeight',
20906             maxProp: 'maxHeight'
20907         },
20908         south : {
20909             orientation: Ext.SplitBar.VERTICAL,
20910             placement: Ext.SplitBar.BOTTOM,
20911             maxFn : 'getVMaxSize',
20912             minProp: 'minHeight',
20913             maxProp: 'maxHeight'
20914         },
20915         east : {
20916             orientation: Ext.SplitBar.HORIZONTAL,
20917             placement: Ext.SplitBar.RIGHT,
20918             maxFn : 'getHMaxSize',
20919             minProp: 'minWidth',
20920             maxProp: 'maxWidth'
20921         },
20922         west : {
20923             orientation: Ext.SplitBar.HORIZONTAL,
20924             placement: Ext.SplitBar.LEFT,
20925             maxFn : 'getHMaxSize',
20926             minProp: 'minWidth',
20927             maxProp: 'maxWidth'
20928         }
20929     },
20930
20931     // private
20932     applyFns : {
20933         west : function(box){
20934             if(this.isCollapsed){
20935                 return this.applyLayoutCollapsed(box);
20936             }
20937             var sd = this.splitEl.dom, s = sd.style;
20938             this.panel.setPosition(box.x, box.y);
20939             var sw = sd.offsetWidth;
20940             s.left = (box.x+box.width-sw)+'px';
20941             s.top = (box.y)+'px';
20942             s.height = Math.max(0, box.height)+'px';
20943             this.panel.setSize(box.width-sw, box.height);
20944         },
20945         east : function(box){
20946             if(this.isCollapsed){
20947                 return this.applyLayoutCollapsed(box);
20948             }
20949             var sd = this.splitEl.dom, s = sd.style;
20950             var sw = sd.offsetWidth;
20951             this.panel.setPosition(box.x+sw, box.y);
20952             s.left = (box.x)+'px';
20953             s.top = (box.y)+'px';
20954             s.height = Math.max(0, box.height)+'px';
20955             this.panel.setSize(box.width-sw, box.height);
20956         },
20957         north : function(box){
20958             if(this.isCollapsed){
20959                 return this.applyLayoutCollapsed(box);
20960             }
20961             var sd = this.splitEl.dom, s = sd.style;
20962             var sh = sd.offsetHeight;
20963             this.panel.setPosition(box.x, box.y);
20964             s.left = (box.x)+'px';
20965             s.top = (box.y+box.height-sh)+'px';
20966             s.width = Math.max(0, box.width)+'px';
20967             this.panel.setSize(box.width, box.height-sh);
20968         },
20969         south : function(box){
20970             if(this.isCollapsed){
20971                 return this.applyLayoutCollapsed(box);
20972             }
20973             var sd = this.splitEl.dom, s = sd.style;
20974             var sh = sd.offsetHeight;
20975             this.panel.setPosition(box.x, box.y+sh);
20976             s.left = (box.x)+'px';
20977             s.top = (box.y)+'px';
20978             s.width = Math.max(0, box.width)+'px';
20979             this.panel.setSize(box.width, box.height-sh);
20980         }
20981     },
20982
20983     // private
20984     render : function(ct, p){
20985         Ext.layout.BorderLayout.SplitRegion.superclass.render.call(this, ct, p);
20986
20987         var ps = this.position;
20988
20989         this.splitEl = ct.createChild({
20990             cls: "x-layout-split x-layout-split-"+ps, html: "&#160;",
20991             id: this.panel.id + '-xsplit'
20992         });
20993
20994         if(this.collapseMode == 'mini'){
20995             this.miniSplitEl = this.splitEl.createChild({
20996                 cls: "x-layout-mini x-layout-mini-"+ps, html: "&#160;"
20997             });
20998             this.miniSplitEl.addClassOnOver('x-layout-mini-over');
20999             this.miniSplitEl.on('click', this.onCollapseClick, this, {stopEvent:true});
21000         }
21001
21002         var s = this.splitSettings[ps];
21003
21004         this.split = new Ext.SplitBar(this.splitEl.dom, p.el, s.orientation);
21005         this.split.tickSize = this.tickSize;
21006         this.split.placement = s.placement;
21007         this.split.getMaximumSize = this[s.maxFn].createDelegate(this);
21008         this.split.minSize = this.minSize || this[s.minProp];
21009         this.split.on("beforeapply", this.onSplitMove, this);
21010         this.split.useShim = this.useShim === true;
21011         this.maxSize = this.maxSize || this[s.maxProp];
21012
21013         if(p.hidden){
21014             this.splitEl.hide();
21015         }
21016
21017         if(this.useSplitTips){
21018             this.splitEl.dom.title = this.collapsible ? this.collapsibleSplitTip : this.splitTip;
21019         }
21020         if(this.collapsible){
21021             this.splitEl.on("dblclick", this.onCollapseClick,  this);
21022         }
21023     },
21024
21025     //docs inherit from superclass
21026     getSize : function(){
21027         if(this.isCollapsed){
21028             return this.collapsedEl.getSize();
21029         }
21030         var s = this.panel.getSize();
21031         if(this.position == 'north' || this.position == 'south'){
21032             s.height += this.splitEl.dom.offsetHeight;
21033         }else{
21034             s.width += this.splitEl.dom.offsetWidth;
21035         }
21036         return s;
21037     },
21038
21039     // private
21040     getHMaxSize : function(){
21041          var cmax = this.maxSize || 10000;
21042          var center = this.layout.center;
21043          return Math.min(cmax, (this.el.getWidth()+center.el.getWidth())-center.getMinWidth());
21044     },
21045
21046     // private
21047     getVMaxSize : function(){
21048         var cmax = this.maxSize || 10000;
21049         var center = this.layout.center;
21050         return Math.min(cmax, (this.el.getHeight()+center.el.getHeight())-center.getMinHeight());
21051     },
21052
21053     // private
21054     onSplitMove : function(split, newSize){
21055         var s = this.panel.getSize();
21056         this.lastSplitSize = newSize;
21057         if(this.position == 'north' || this.position == 'south'){
21058             this.panel.setSize(s.width, newSize);
21059             this.state.height = newSize;
21060         }else{
21061             this.panel.setSize(newSize, s.height);
21062             this.state.width = newSize;
21063         }
21064         this.layout.layout();
21065         this.panel.saveState();
21066         return false;
21067     },
21068
21069     /**
21070      * Returns a reference to the split bar in use by this region.
21071      * @return {Ext.SplitBar} The split bar
21072      */
21073     getSplitBar : function(){
21074         return this.split;
21075     },
21076
21077     // inherit docs
21078     destroy : function() {
21079         Ext.destroy(this.miniSplitEl, this.split, this.splitEl);
21080         Ext.layout.BorderLayout.SplitRegion.superclass.destroy.call(this);
21081     }
21082 });
21083
21084 Ext.Container.LAYOUTS['border'] = Ext.layout.BorderLayout;/**
21085  * @class Ext.layout.FormLayout
21086  * @extends Ext.layout.AnchorLayout
21087  * <p>This layout manager is specifically designed for rendering and managing child Components of
21088  * {@link Ext.form.FormPanel forms}. It is responsible for rendering the labels of
21089  * {@link Ext.form.Field Field}s.</p>
21090  *
21091  * <p>This layout manager is used when a Container is configured with the <tt>layout:'form'</tt>
21092  * {@link Ext.Container#layout layout} config option, and should generally not need to be created directly
21093  * via the new keyword. See <tt><b>{@link Ext.Container#layout}</b></tt> for additional details.</p>
21094  *
21095  * <p>In an application, it will usually be preferrable to use a {@link Ext.form.FormPanel FormPanel}
21096  * (which is configured with FormLayout as its layout class by default) since it also provides built-in
21097  * functionality for {@link Ext.form.BasicForm#doAction loading, validating and submitting} the form.</p>
21098  *
21099  * <p>A {@link Ext.Container Container} <i>using</i> the FormLayout layout manager (e.g.
21100  * {@link Ext.form.FormPanel} or specifying <tt>layout:'form'</tt>) can also accept the following
21101  * layout-specific config properties:<div class="mdetail-params"><ul>
21102  * <li><b><tt>{@link Ext.form.FormPanel#hideLabels hideLabels}</tt></b></li>
21103  * <li><b><tt>{@link Ext.form.FormPanel#labelAlign labelAlign}</tt></b></li>
21104  * <li><b><tt>{@link Ext.form.FormPanel#labelPad labelPad}</tt></b></li>
21105  * <li><b><tt>{@link Ext.form.FormPanel#labelSeparator labelSeparator}</tt></b></li>
21106  * <li><b><tt>{@link Ext.form.FormPanel#labelWidth labelWidth}</tt></b></li>
21107  * </ul></div></p>
21108  *
21109  * <p>Any Component (including Fields) managed by FormLayout accepts the following as a config option:
21110  * <div class="mdetail-params"><ul>
21111  * <li><b><tt>{@link Ext.Component#anchor anchor}</tt></b></li>
21112  * </ul></div></p>
21113  *
21114  * <p>Any Component managed by FormLayout may be rendered as a form field (with an associated label) by
21115  * configuring it with a non-null <b><tt>{@link Ext.Component#fieldLabel fieldLabel}</tt></b>. Components configured
21116  * in this way may be configured with the following options which affect the way the FormLayout renders them:
21117  * <div class="mdetail-params"><ul>
21118  * <li><b><tt>{@link Ext.Component#clearCls clearCls}</tt></b></li>
21119  * <li><b><tt>{@link Ext.Component#fieldLabel fieldLabel}</tt></b></li>
21120  * <li><b><tt>{@link Ext.Component#hideLabel hideLabel}</tt></b></li>
21121  * <li><b><tt>{@link Ext.Component#itemCls itemCls}</tt></b></li>
21122  * <li><b><tt>{@link Ext.Component#labelSeparator labelSeparator}</tt></b></li>
21123  * <li><b><tt>{@link Ext.Component#labelStyle labelStyle}</tt></b></li>
21124  * </ul></div></p>
21125  *
21126  * <p>Example usage:</p>
21127  * <pre><code>
21128 // Required if showing validation messages
21129 Ext.QuickTips.init();
21130
21131 // While you can create a basic Panel with layout:'form', practically
21132 // you should usually use a FormPanel to also get its form functionality
21133 // since it already creates a FormLayout internally.
21134 var form = new Ext.form.FormPanel({
21135     title: 'Form Layout',
21136     bodyStyle: 'padding:15px',
21137     width: 350,
21138     defaultType: 'textfield',
21139     defaults: {
21140         // applied to each contained item
21141         width: 230,
21142         msgTarget: 'side'
21143     },
21144     items: [{
21145             fieldLabel: 'First Name',
21146             name: 'first',
21147             allowBlank: false,
21148             {@link Ext.Component#labelSeparator labelSeparator}: ':' // override labelSeparator layout config
21149         },{
21150             fieldLabel: 'Last Name',
21151             name: 'last'
21152         },{
21153             fieldLabel: 'Email',
21154             name: 'email',
21155             vtype:'email'
21156         }, {
21157             xtype: 'textarea',
21158             hideLabel: true,     // override hideLabels layout config
21159             name: 'msg',
21160             anchor: '100% -53'
21161         }
21162     ],
21163     buttons: [
21164         {text: 'Save'},
21165         {text: 'Cancel'}
21166     ],
21167     layoutConfig: {
21168         {@link #labelSeparator}: '~' // superseded by assignment below
21169     },
21170     // config options applicable to container when layout='form':
21171     hideLabels: false,
21172     labelAlign: 'left',   // or 'right' or 'top'
21173     {@link Ext.form.FormPanel#labelSeparator labelSeparator}: '>>', // takes precedence over layoutConfig value
21174     labelWidth: 65,       // defaults to 100
21175     labelPad: 8           // defaults to 5, must specify labelWidth to be honored
21176 });
21177 </code></pre>
21178  */
21179 Ext.layout.FormLayout = Ext.extend(Ext.layout.AnchorLayout, {
21180
21181     /**
21182      * @cfg {String} labelSeparator
21183      * See {@link Ext.form.FormPanel}.{@link Ext.form.FormPanel#labelSeparator labelSeparator}.  Configuration
21184      * of this property at the <b>container</b> level takes precedence.
21185      */
21186     labelSeparator : ':',
21187
21188     /**
21189      * Read only. The CSS style specification string added to field labels in this layout if not
21190      * otherwise {@link Ext.Component#labelStyle specified by each contained field}.
21191      * @type String
21192      * @property labelStyle
21193      */
21194
21195     /**
21196      * @cfg {Boolean} trackLabels
21197      * True to show/hide the field label when the field is hidden. Defaults to <tt>false</tt>.
21198      */
21199     trackLabels: false,
21200
21201     type: 'form',
21202
21203
21204     onRemove: function(c){
21205         Ext.layout.FormLayout.superclass.onRemove.call(this, c);
21206         if(this.trackLabels){
21207             c.un('show', this.onFieldShow, this);
21208             c.un('hide', this.onFieldHide, this);
21209         }
21210         // check for itemCt, since we may be removing a fieldset or something similar
21211         var el = c.getPositionEl(),
21212                 ct = c.getItemCt && c.getItemCt();
21213         if(c.rendered && ct){
21214             if (el && el.dom) {
21215                 el.insertAfter(ct);
21216             }
21217             Ext.destroy(ct);
21218             Ext.destroyMembers(c, 'label', 'itemCt');
21219             if(c.customItemCt){
21220                 Ext.destroyMembers(c, 'getItemCt', 'customItemCt');
21221             }
21222         }
21223     },
21224
21225     // private
21226     setContainer : function(ct){
21227         Ext.layout.FormLayout.superclass.setContainer.call(this, ct);
21228         if(ct.labelAlign){
21229             ct.addClass('x-form-label-'+ct.labelAlign);
21230         }
21231
21232         if(ct.hideLabels){
21233             Ext.apply(this, {
21234                 labelStyle: 'display:none',
21235                 elementStyle: 'padding-left:0;',
21236                 labelAdjust: 0
21237             });
21238         }else{
21239             this.labelSeparator = ct.labelSeparator || this.labelSeparator;
21240             ct.labelWidth = ct.labelWidth || 100;
21241             if(Ext.isNumber(ct.labelWidth)){
21242                 var pad = Ext.isNumber(ct.labelPad) ? ct.labelPad : 5;
21243                 Ext.apply(this, {
21244                     labelAdjust: ct.labelWidth + pad,
21245                     labelStyle: 'width:' + ct.labelWidth + 'px;',
21246                     elementStyle: 'padding-left:' + (ct.labelWidth + pad) + 'px'
21247                 });
21248             }
21249             if(ct.labelAlign == 'top'){
21250                 Ext.apply(this, {
21251                     labelStyle: 'width:auto;',
21252                     labelAdjust: 0,
21253                     elementStyle: 'padding-left:0;'
21254                 });
21255             }
21256         }
21257     },
21258
21259     // private
21260     isHide: function(c){
21261         return c.hideLabel || this.container.hideLabels;
21262     },
21263
21264     onFieldShow: function(c){
21265         c.getItemCt().removeClass('x-hide-' + c.hideMode);
21266     },
21267
21268     onFieldHide: function(c){
21269         c.getItemCt().addClass('x-hide-' + c.hideMode);
21270     },
21271
21272     //private
21273     getLabelStyle: function(s){
21274         var ls = '', items = [this.labelStyle, s];
21275         for (var i = 0, len = items.length; i < len; ++i){
21276             if (items[i]){
21277                 ls += items[i];
21278                 if (ls.substr(-1, 1) != ';'){
21279                     ls += ';'
21280                 }
21281             }
21282         }
21283         return ls;
21284     },
21285
21286     /**
21287      * @cfg {Ext.Template} fieldTpl
21288      * A {@link Ext.Template#compile compile}d {@link Ext.Template} for rendering
21289      * the fully wrapped, labeled and styled form Field. Defaults to:</p><pre><code>
21290 new Ext.Template(
21291     &#39;&lt;div class="x-form-item {itemCls}" tabIndex="-1">&#39;,
21292         &#39;&lt;&#108;abel for="{id}" style="{labelStyle}" class="x-form-item-&#108;abel">{&#108;abel}{labelSeparator}&lt;/&#108;abel>&#39;,
21293         &#39;&lt;div class="x-form-element" id="x-form-el-{id}" style="{elementStyle}">&#39;,
21294         &#39;&lt;/div>&lt;div class="{clearCls}">&lt;/div>&#39;,
21295     '&lt;/div>'
21296 );
21297 </code></pre>
21298      * <p>This may be specified to produce a different DOM structure when rendering form Fields.</p>
21299      * <p>A description of the properties within the template follows:</p><div class="mdetail-params"><ul>
21300      * <li><b><tt>itemCls</tt></b> : String<div class="sub-desc">The CSS class applied to the outermost div wrapper
21301      * that contains this field label and field element (the default class is <tt>'x-form-item'</tt> and <tt>itemCls</tt>
21302      * will be added to that). If supplied, <tt>itemCls</tt> at the field level will override the default <tt>itemCls</tt>
21303      * supplied at the container level.</div></li>
21304      * <li><b><tt>id</tt></b> : String<div class="sub-desc">The id of the Field</div></li>
21305      * <li><b><tt>{@link #labelStyle}</tt></b> : String<div class="sub-desc">
21306      * A CSS style specification string to add to the field label for this field (defaults to <tt>''</tt> or the
21307      * {@link #labelStyle layout's value for <tt>labelStyle</tt>}).</div></li>
21308      * <li><b><tt>label</tt></b> : String<div class="sub-desc">The text to display as the label for this
21309      * field (defaults to <tt>''</tt>)</div></li>
21310      * <li><b><tt>{@link #labelSeparator}</tt></b> : String<div class="sub-desc">The separator to display after
21311      * the text of the label for this field (defaults to a colon <tt>':'</tt> or the
21312      * {@link #labelSeparator layout's value for labelSeparator}). To hide the separator use empty string ''.</div></li>
21313      * <li><b><tt>elementStyle</tt></b> : String<div class="sub-desc">The styles text for the input element's wrapper.</div></li>
21314      * <li><b><tt>clearCls</tt></b> : String<div class="sub-desc">The CSS class to apply to the special clearing div
21315      * rendered directly after each form field wrapper (defaults to <tt>'x-form-clear-left'</tt>)</div></li>
21316      * </ul></div>
21317      * <p>Also see <tt>{@link #getTemplateArgs}</tt></p>
21318      */
21319
21320     // private
21321     renderItem : function(c, position, target){
21322         if(c && (c.isFormField || c.fieldLabel) && c.inputType != 'hidden'){
21323             var args = this.getTemplateArgs(c);
21324             if(Ext.isNumber(position)){
21325                 position = target.dom.childNodes[position] || null;
21326             }
21327             if(position){
21328                 c.itemCt = this.fieldTpl.insertBefore(position, args, true);
21329             }else{
21330                 c.itemCt = this.fieldTpl.append(target, args, true);
21331             }
21332             if(!c.getItemCt){
21333                 // Non form fields don't have getItemCt, apply it here
21334                 // This will get cleaned up in onRemove
21335                 Ext.apply(c, {
21336                     getItemCt: function(){
21337                         return c.itemCt;
21338                     },
21339                     customItemCt: true
21340                 });
21341             }
21342             c.label = c.getItemCt().child('label.x-form-item-label');
21343             if(!c.rendered){
21344                 c.render('x-form-el-' + c.id);
21345             }else if(!this.isValidParent(c, target)){
21346                 Ext.fly('x-form-el-' + c.id).appendChild(c.getPositionEl());
21347             }
21348             if(this.trackLabels){
21349                 if(c.hidden){
21350                     this.onFieldHide(c);
21351                 }
21352                 c.on({
21353                     scope: this,
21354                     show: this.onFieldShow,
21355                     hide: this.onFieldHide
21356                 });
21357             }
21358             this.configureItem(c);
21359         }else {
21360             Ext.layout.FormLayout.superclass.renderItem.apply(this, arguments);
21361         }
21362     },
21363
21364     /**
21365      * <p>Provides template arguments for rendering the fully wrapped, labeled and styled form Field.</p>
21366      * <p>This method returns an object hash containing properties used by the layout's {@link #fieldTpl}
21367      * to create a correctly wrapped, labeled and styled form Field. This may be overriden to
21368      * create custom layouts. The properties which must be returned are:</p><div class="mdetail-params"><ul>
21369      * <li><b><tt>itemCls</tt></b> : String<div class="sub-desc">The CSS class applied to the outermost div wrapper
21370      * that contains this field label and field element (the default class is <tt>'x-form-item'</tt> and <tt>itemCls</tt>
21371      * will be added to that). If supplied, <tt>itemCls</tt> at the field level will override the default <tt>itemCls</tt>
21372      * supplied at the container level.</div></li>
21373      * <li><b><tt>id</tt></b> : String<div class="sub-desc">The id of the Field</div></li>
21374      * <li><b><tt>{@link #labelStyle}</tt></b> : String<div class="sub-desc">
21375      * A CSS style specification string to add to the field label for this field (defaults to <tt>''</tt> or the
21376      * {@link #labelStyle layout's value for <tt>labelStyle</tt>}).</div></li>
21377      * <li><b><tt>label</tt></b> : String<div class="sub-desc">The text to display as the label for this
21378      * field (defaults to <tt>''</tt>)</div></li>
21379      * <li><b><tt>{@link #labelSeparator}</tt></b> : String<div class="sub-desc">The separator to display after
21380      * the text of the label for this field (defaults to a colon <tt>':'</tt> or the
21381      * {@link #labelSeparator layout's value for labelSeparator}). To hide the separator use empty string ''.</div></li>
21382      * <li><b><tt>elementStyle</tt></b> : String<div class="sub-desc">The styles text for the input element's wrapper.</div></li>
21383      * <li><b><tt>clearCls</tt></b> : String<div class="sub-desc">The CSS class to apply to the special clearing div
21384      * rendered directly after each form field wrapper (defaults to <tt>'x-form-clear-left'</tt>)</div></li>
21385      * </ul></div>
21386      * @param (Ext.form.Field} field The {@link Ext.form.Field Field} being rendered.
21387      * @return An object hash containing the properties required to render the Field.
21388      */
21389     getTemplateArgs: function(field) {
21390         var noLabelSep = !field.fieldLabel || field.hideLabel;
21391         return {
21392             id: field.id,
21393             label: field.fieldLabel,
21394             labelStyle: this.getLabelStyle(field.labelStyle),
21395             elementStyle: this.elementStyle||'',
21396             labelSeparator: noLabelSep ? '' : (Ext.isDefined(field.labelSeparator) ? field.labelSeparator : this.labelSeparator),
21397             itemCls: (field.itemCls||this.container.itemCls||'') + (field.hideLabel ? ' x-hide-label' : ''),
21398             clearCls: field.clearCls || 'x-form-clear-left'
21399         };
21400     },
21401
21402     // private
21403     adjustWidthAnchor: function(value, c){
21404         if(c.label && !this.isHide(c) && (this.container.labelAlign != 'top')){
21405             var adjust = Ext.isIE6 || (Ext.isIE && !Ext.isStrict);
21406             return value - this.labelAdjust + (adjust ? -3 : 0);
21407         }
21408         return value;
21409     },
21410
21411     adjustHeightAnchor : function(value, c){
21412         if(c.label && !this.isHide(c) && (this.container.labelAlign == 'top')){
21413             return value - c.label.getHeight();
21414         }
21415         return value;
21416     },
21417
21418     // private
21419     isValidParent : function(c, target){
21420         return target && this.container.getEl().contains(c.getPositionEl());
21421     }
21422
21423     /**
21424      * @property activeItem
21425      * @hide
21426      */
21427 });
21428
21429 Ext.Container.LAYOUTS['form'] = Ext.layout.FormLayout;
21430 /**\r
21431  * @class Ext.layout.AccordionLayout\r
21432  * @extends Ext.layout.FitLayout\r
21433  * <p>This is a layout that manages multiple Panels in an expandable accordion style such that only\r
21434  * <b>one Panel can be expanded at any given time</b>. Each Panel has built-in support for expanding and collapsing.</p>\r
21435  * <p>Note: Only Ext.Panels <b>and all subclasses of Ext.Panel</b> may be used in an accordion layout Container.</p>\r
21436  * <p>This class is intended to be extended or created via the <tt><b>{@link Ext.Container#layout layout}</b></tt>\r
21437  * configuration property.  See <tt><b>{@link Ext.Container#layout}</b></tt> for additional details.</p>\r
21438  * <p>Example usage:</p>\r
21439  * <pre><code>\r
21440 var accordion = new Ext.Panel({\r
21441     title: 'Accordion Layout',\r
21442     layout:'accordion',\r
21443     defaults: {\r
21444         // applied to each contained panel\r
21445         bodyStyle: 'padding:15px'\r
21446     },\r
21447     layoutConfig: {\r
21448         // layout-specific configs go here\r
21449         titleCollapse: false,\r
21450         animate: true,\r
21451         activeOnTop: true\r
21452     },\r
21453     items: [{\r
21454         title: 'Panel 1',\r
21455         html: '&lt;p&gt;Panel content!&lt;/p&gt;'\r
21456     },{\r
21457         title: 'Panel 2',\r
21458         html: '&lt;p&gt;Panel content!&lt;/p&gt;'\r
21459     },{\r
21460         title: 'Panel 3',\r
21461         html: '&lt;p&gt;Panel content!&lt;/p&gt;'\r
21462     }]\r
21463 });\r
21464 </code></pre>\r
21465  */\r
21466 Ext.layout.AccordionLayout = Ext.extend(Ext.layout.FitLayout, {\r
21467     /**\r
21468      * @cfg {Boolean} fill\r
21469      * True to adjust the active item's height to fill the available space in the container, false to use the\r
21470      * item's current height, or auto height if not explicitly set (defaults to true).\r
21471      */\r
21472     fill : true,\r
21473     /**\r
21474      * @cfg {Boolean} autoWidth\r
21475      * True to set each contained item's width to 'auto', false to use the item's current width (defaults to true).\r
21476      * Note that some components, in particular the {@link Ext.grid.GridPanel grid}, will not function properly within\r
21477      * layouts if they have auto width, so in such cases this config should be set to false.\r
21478      */\r
21479     autoWidth : true,\r
21480     /**\r
21481      * @cfg {Boolean} titleCollapse\r
21482      * True to allow expand/collapse of each contained panel by clicking anywhere on the title bar, false to allow\r
21483      * expand/collapse only when the toggle tool button is clicked (defaults to true).  When set to false,\r
21484      * {@link #hideCollapseTool} should be false also.\r
21485      */\r
21486     titleCollapse : true,\r
21487     /**\r
21488      * @cfg {Boolean} hideCollapseTool\r
21489      * True to hide the contained panels' collapse/expand toggle buttons, false to display them (defaults to false).\r
21490      * When set to true, {@link #titleCollapse} should be true also.\r
21491      */\r
21492     hideCollapseTool : false,\r
21493     /**\r
21494      * @cfg {Boolean} collapseFirst\r
21495      * True to make sure the collapse/expand toggle button always renders first (to the left of) any other tools\r
21496      * in the contained panels' title bars, false to render it last (defaults to false).\r
21497      */\r
21498     collapseFirst : false,\r
21499     /**\r
21500      * @cfg {Boolean} animate\r
21501      * True to slide the contained panels open and closed during expand/collapse using animation, false to open and\r
21502      * close directly with no animation (defaults to false).  Note: to defer to the specific config setting of each\r
21503      * contained panel for this property, set this to undefined at the layout level.\r
21504      */\r
21505     animate : false,\r
21506     /**\r
21507      * @cfg {Boolean} sequence\r
21508      * <b>Experimental</b>. If animate is set to true, this will result in each animation running in sequence.\r
21509      */\r
21510     sequence : false,\r
21511     /**\r
21512      * @cfg {Boolean} activeOnTop\r
21513      * True to swap the position of each panel as it is expanded so that it becomes the first item in the container,\r
21514      * false to keep the panels in the rendered order. <b>This is NOT compatible with "animate:true"</b> (defaults to false).\r
21515      */\r
21516     activeOnTop : false,\r
21517 \r
21518     type: 'accordion',\r
21519 \r
21520     renderItem : function(c){\r
21521         if(this.animate === false){\r
21522             c.animCollapse = false;\r
21523         }\r
21524         c.collapsible = true;\r
21525         if(this.autoWidth){\r
21526             c.autoWidth = true;\r
21527         }\r
21528         if(this.titleCollapse){\r
21529             c.titleCollapse = true;\r
21530         }\r
21531         if(this.hideCollapseTool){\r
21532             c.hideCollapseTool = true;\r
21533         }\r
21534         if(this.collapseFirst !== undefined){\r
21535             c.collapseFirst = this.collapseFirst;\r
21536         }\r
21537         if(!this.activeItem && !c.collapsed){\r
21538             this.setActiveItem(c, true);\r
21539         }else if(this.activeItem && this.activeItem != c){\r
21540             c.collapsed = true;\r
21541         }\r
21542         Ext.layout.AccordionLayout.superclass.renderItem.apply(this, arguments);\r
21543         c.header.addClass('x-accordion-hd');\r
21544         c.on('beforeexpand', this.beforeExpand, this);\r
21545     },\r
21546 \r
21547     onRemove: function(c){\r
21548         Ext.layout.AccordionLayout.superclass.onRemove.call(this, c);\r
21549         if(c.rendered){\r
21550             c.header.removeClass('x-accordion-hd');\r
21551         }\r
21552         c.un('beforeexpand', this.beforeExpand, this);\r
21553     },\r
21554 \r
21555     // private\r
21556     beforeExpand : function(p, anim){\r
21557         var ai = this.activeItem;\r
21558         if(ai){\r
21559             if(this.sequence){\r
21560                 delete this.activeItem;\r
21561                 if (!ai.collapsed){\r
21562                     ai.collapse({callback:function(){\r
21563                         p.expand(anim || true);\r
21564                     }, scope: this});\r
21565                     return false;\r
21566                 }\r
21567             }else{\r
21568                 ai.collapse(this.animate);\r
21569             }\r
21570         }\r
21571         this.setActive(p);\r
21572         if(this.activeOnTop){\r
21573             p.el.dom.parentNode.insertBefore(p.el.dom, p.el.dom.parentNode.firstChild);\r
21574         }\r
21575         // Items have been hidden an possibly rearranged, we need to get the container size again.\r
21576         this.layout();\r
21577     },\r
21578 \r
21579     // private\r
21580     setItemSize : function(item, size){\r
21581         if(this.fill && item){\r
21582             var hh = 0, i, ct = this.getRenderedItems(this.container), len = ct.length, p;\r
21583             // Add up all the header heights\r
21584             for (i = 0; i < len; i++) {\r
21585                 if((p = ct[i]) != item){\r
21586                     hh += p.header.getHeight();\r
21587                 }\r
21588             };\r
21589             // Subtract the header heights from the container size\r
21590             size.height -= hh;\r
21591             // Call setSize on the container to set the correct height.  For Panels, deferedHeight\r
21592             // will simply store this size for when the expansion is done.\r
21593             item.setSize(size);\r
21594         }\r
21595     },\r
21596 \r
21597     /**\r
21598      * Sets the active (expanded) item in the layout.\r
21599      * @param {String/Number} item The string component id or numeric index of the item to activate\r
21600      */\r
21601     setActiveItem : function(item){\r
21602         this.setActive(item, true);\r
21603     },\r
21604 \r
21605     // private\r
21606     setActive : function(item, expand){\r
21607         var ai = this.activeItem;\r
21608         item = this.container.getComponent(item);\r
21609         if(ai != item){\r
21610             if(item.rendered && item.collapsed && expand){\r
21611                 item.expand();\r
21612             }else{\r
21613                 if(ai){\r
21614                    ai.fireEvent('deactivate', ai);\r
21615                 }\r
21616                 this.activeItem = item;\r
21617                 item.fireEvent('activate', item);\r
21618             }\r
21619         }\r
21620     }\r
21621 });\r
21622 Ext.Container.LAYOUTS.accordion = Ext.layout.AccordionLayout;\r
21623 \r
21624 //backwards compat\r
21625 Ext.layout.Accordion = Ext.layout.AccordionLayout;/**\r
21626  * @class Ext.layout.TableLayout\r
21627  * @extends Ext.layout.ContainerLayout\r
21628  * <p>This layout allows you to easily render content into an HTML table.  The total number of columns can be\r
21629  * specified, and rowspan and colspan can be used to create complex layouts within the table.\r
21630  * This class is intended to be extended or created via the layout:'table' {@link Ext.Container#layout} config,\r
21631  * and should generally not need to be created directly via the new keyword.</p>\r
21632  * <p>Note that when creating a layout via config, the layout-specific config properties must be passed in via\r
21633  * the {@link Ext.Container#layoutConfig} object which will then be applied internally to the layout.  In the\r
21634  * case of TableLayout, the only valid layout config property is {@link #columns}.  However, the items added to a\r
21635  * TableLayout can supply the following table-specific config properties:</p>\r
21636  * <ul>\r
21637  * <li><b>rowspan</b> Applied to the table cell containing the item.</li>\r
21638  * <li><b>colspan</b> Applied to the table cell containing the item.</li>\r
21639  * <li><b>cellId</b> An id applied to the table cell containing the item.</li>\r
21640  * <li><b>cellCls</b> A CSS class name added to the table cell containing the item.</li>\r
21641  * </ul>\r
21642  * <p>The basic concept of building up a TableLayout is conceptually very similar to building up a standard\r
21643  * HTML table.  You simply add each panel (or "cell") that you want to include along with any span attributes\r
21644  * specified as the special config properties of rowspan and colspan which work exactly like their HTML counterparts.\r
21645  * Rather than explicitly creating and nesting rows and columns as you would in HTML, you simply specify the\r
21646  * total column count in the layoutConfig and start adding panels in their natural order from left to right,\r
21647  * top to bottom.  The layout will automatically figure out, based on the column count, rowspans and colspans,\r
21648  * how to position each panel within the table.  Just like with HTML tables, your rowspans and colspans must add\r
21649  * up correctly in your overall layout or you'll end up with missing and/or extra cells!  Example usage:</p>\r
21650  * <pre><code>\r
21651 // This code will generate a layout table that is 3 columns by 2 rows\r
21652 // with some spanning included.  The basic layout will be:\r
21653 // +--------+-----------------+\r
21654 // |   A    |   B             |\r
21655 // |        |--------+--------|\r
21656 // |        |   C    |   D    |\r
21657 // +--------+--------+--------+\r
21658 var table = new Ext.Panel({\r
21659     title: 'Table Layout',\r
21660     layout:'table',\r
21661     defaults: {\r
21662         // applied to each contained panel\r
21663         bodyStyle:'padding:20px'\r
21664     },\r
21665     layoutConfig: {\r
21666         // The total column count must be specified here\r
21667         columns: 3\r
21668     },\r
21669     items: [{\r
21670         html: '&lt;p&gt;Cell A content&lt;/p&gt;',\r
21671         rowspan: 2\r
21672     },{\r
21673         html: '&lt;p&gt;Cell B content&lt;/p&gt;',\r
21674         colspan: 2\r
21675     },{\r
21676         html: '&lt;p&gt;Cell C content&lt;/p&gt;',\r
21677         cellCls: 'highlight'\r
21678     },{\r
21679         html: '&lt;p&gt;Cell D content&lt;/p&gt;'\r
21680     }]\r
21681 });\r
21682 </code></pre>\r
21683  */\r
21684 Ext.layout.TableLayout = Ext.extend(Ext.layout.ContainerLayout, {\r
21685     /**\r
21686      * @cfg {Number} columns\r
21687      * The total number of columns to create in the table for this layout.  If not specified, all Components added to\r
21688      * this layout will be rendered into a single row using one column per Component.\r
21689      */\r
21690 \r
21691     // private\r
21692     monitorResize:false,\r
21693 \r
21694     type: 'table',\r
21695 \r
21696     targetCls: 'x-table-layout-ct',\r
21697 \r
21698     /**\r
21699      * @cfg {Object} tableAttrs\r
21700      * <p>An object containing properties which are added to the {@link Ext.DomHelper DomHelper} specification\r
21701      * used to create the layout's <tt>&lt;table&gt;</tt> element. Example:</p><pre><code>\r
21702 {\r
21703     xtype: 'panel',\r
21704     layout: 'table',\r
21705     layoutConfig: {\r
21706         tableAttrs: {\r
21707             style: {\r
21708                 width: '100%'\r
21709             }\r
21710         },\r
21711         columns: 3\r
21712     }\r
21713 }</code></pre>\r
21714      */\r
21715     tableAttrs:null,\r
21716 \r
21717     // private\r
21718     setContainer : function(ct){\r
21719         Ext.layout.TableLayout.superclass.setContainer.call(this, ct);\r
21720 \r
21721         this.currentRow = 0;\r
21722         this.currentColumn = 0;\r
21723         this.cells = [];\r
21724     },\r
21725     \r
21726     // private\r
21727     onLayout : function(ct, target){\r
21728         var cs = ct.items.items, len = cs.length, c, i;\r
21729 \r
21730         if(!this.table){\r
21731             target.addClass('x-table-layout-ct');\r
21732 \r
21733             this.table = target.createChild(\r
21734                 Ext.apply({tag:'table', cls:'x-table-layout', cellspacing: 0, cn: {tag: 'tbody'}}, this.tableAttrs), null, true);\r
21735         }\r
21736         this.renderAll(ct, target);\r
21737     },\r
21738 \r
21739     // private\r
21740     getRow : function(index){\r
21741         var row = this.table.tBodies[0].childNodes[index];\r
21742         if(!row){\r
21743             row = document.createElement('tr');\r
21744             this.table.tBodies[0].appendChild(row);\r
21745         }\r
21746         return row;\r
21747     },\r
21748 \r
21749     // private\r
21750     getNextCell : function(c){\r
21751         var cell = this.getNextNonSpan(this.currentColumn, this.currentRow);\r
21752         var curCol = this.currentColumn = cell[0], curRow = this.currentRow = cell[1];\r
21753         for(var rowIndex = curRow; rowIndex < curRow + (c.rowspan || 1); rowIndex++){\r
21754             if(!this.cells[rowIndex]){\r
21755                 this.cells[rowIndex] = [];\r
21756             }\r
21757             for(var colIndex = curCol; colIndex < curCol + (c.colspan || 1); colIndex++){\r
21758                 this.cells[rowIndex][colIndex] = true;\r
21759             }\r
21760         }\r
21761         var td = document.createElement('td');\r
21762         if(c.cellId){\r
21763             td.id = c.cellId;\r
21764         }\r
21765         var cls = 'x-table-layout-cell';\r
21766         if(c.cellCls){\r
21767             cls += ' ' + c.cellCls;\r
21768         }\r
21769         td.className = cls;\r
21770         if(c.colspan){\r
21771             td.colSpan = c.colspan;\r
21772         }\r
21773         if(c.rowspan){\r
21774             td.rowSpan = c.rowspan;\r
21775         }\r
21776         this.getRow(curRow).appendChild(td);\r
21777         return td;\r
21778     },\r
21779 \r
21780     // private\r
21781     getNextNonSpan: function(colIndex, rowIndex){\r
21782         var cols = this.columns;\r
21783         while((cols && colIndex >= cols) || (this.cells[rowIndex] && this.cells[rowIndex][colIndex])) {\r
21784             if(cols && colIndex >= cols){\r
21785                 rowIndex++;\r
21786                 colIndex = 0;\r
21787             }else{\r
21788                 colIndex++;\r
21789             }\r
21790         }\r
21791         return [colIndex, rowIndex];\r
21792     },\r
21793 \r
21794     // private\r
21795     renderItem : function(c, position, target){\r
21796         // Ensure we have our inner table to get cells to render into.\r
21797         if(!this.table){\r
21798             this.table = target.createChild(\r
21799                 Ext.apply({tag:'table', cls:'x-table-layout', cellspacing: 0, cn: {tag: 'tbody'}}, this.tableAttrs), null, true);\r
21800         }\r
21801         if(c && !c.rendered){\r
21802             c.render(this.getNextCell(c));\r
21803             this.configureItem(c, position);\r
21804         }else if(c && !this.isValidParent(c, target)){\r
21805             var container = this.getNextCell(c);\r
21806             container.insertBefore(c.getPositionEl().dom, null);\r
21807             c.container = Ext.get(container);\r
21808             this.configureItem(c, position);\r
21809         }\r
21810     },\r
21811 \r
21812     // private\r
21813     isValidParent : function(c, target){\r
21814         return c.getPositionEl().up('table', 5).dom.parentNode === (target.dom || target);\r
21815     }\r
21816 \r
21817     /**\r
21818      * @property activeItem\r
21819      * @hide\r
21820      */\r
21821 });\r
21822 \r
21823 Ext.Container.LAYOUTS['table'] = Ext.layout.TableLayout;/**
21824  * @class Ext.layout.AbsoluteLayout
21825  * @extends Ext.layout.AnchorLayout
21826  * <p>This is a layout that inherits the anchoring of <b>{@link Ext.layout.AnchorLayout}</b> and adds the
21827  * ability for x/y positioning using the standard x and y component config options.</p>
21828  * <p>This class is intended to be extended or created via the <tt><b>{@link Ext.Container#layout layout}</b></tt>
21829  * configuration property.  See <tt><b>{@link Ext.Container#layout}</b></tt> for additional details.</p>
21830  * <p>Example usage:</p>
21831  * <pre><code>
21832 var form = new Ext.form.FormPanel({
21833     title: 'Absolute Layout',
21834     layout:'absolute',
21835     layoutConfig: {
21836         // layout-specific configs go here
21837         extraCls: 'x-abs-layout-item',
21838     },
21839     baseCls: 'x-plain',
21840     url:'save-form.php',
21841     defaultType: 'textfield',
21842     items: [{
21843         x: 0,
21844         y: 5,
21845         xtype:'label',
21846         text: 'Send To:'
21847     },{
21848         x: 60,
21849         y: 0,
21850         name: 'to',
21851         anchor:'100%'  // anchor width by percentage
21852     },{
21853         x: 0,
21854         y: 35,
21855         xtype:'label',
21856         text: 'Subject:'
21857     },{
21858         x: 60,
21859         y: 30,
21860         name: 'subject',
21861         anchor: '100%'  // anchor width by percentage
21862     },{
21863         x:0,
21864         y: 60,
21865         xtype: 'textarea',
21866         name: 'msg',
21867         anchor: '100% 100%'  // anchor width and height
21868     }]
21869 });
21870 </code></pre>
21871  */
21872 Ext.layout.AbsoluteLayout = Ext.extend(Ext.layout.AnchorLayout, {
21873
21874     extraCls: 'x-abs-layout-item',
21875
21876     type: 'anchor',
21877
21878     onLayout : function(ct, target){
21879         target.position();
21880         this.paddingLeft = target.getPadding('l');
21881         this.paddingTop = target.getPadding('t');
21882         Ext.layout.AbsoluteLayout.superclass.onLayout.call(this, ct, target);
21883     },
21884
21885     // private
21886     adjustWidthAnchor : function(value, comp){
21887         return value ? value - comp.getPosition(true)[0] + this.paddingLeft : value;
21888     },
21889
21890     // private
21891     adjustHeightAnchor : function(value, comp){
21892         return  value ? value - comp.getPosition(true)[1] + this.paddingTop : value;
21893     }
21894     /**
21895      * @property activeItem
21896      * @hide
21897      */
21898 });
21899 Ext.Container.LAYOUTS['absolute'] = Ext.layout.AbsoluteLayout;
21900 /**
21901  * @class Ext.layout.BoxLayout
21902  * @extends Ext.layout.ContainerLayout
21903  * <p>Base Class for HBoxLayout and VBoxLayout Classes. Generally it should not need to be used directly.</p>
21904  */
21905 Ext.layout.BoxLayout = Ext.extend(Ext.layout.ContainerLayout, {
21906     /**
21907      * @cfg {Object} defaultMargins
21908      * <p>If the individual contained items do not have a <tt>margins</tt>
21909      * property specified, the default margins from this property will be
21910      * applied to each item.</p>
21911      * <br><p>This property may be specified as an object containing margins
21912      * to apply in the format:</p><pre><code>
21913 {
21914     top: (top margin),
21915     right: (right margin),
21916     bottom: (bottom margin),
21917     left: (left margin)
21918 }</code></pre>
21919      * <p>This property may also be specified as a string containing
21920      * space-separated, numeric margin values. The order of the sides associated
21921      * with each value matches the way CSS processes margin values:</p>
21922      * <div class="mdetail-params"><ul>
21923      * <li>If there is only one value, it applies to all sides.</li>
21924      * <li>If there are two values, the top and bottom borders are set to the
21925      * first value and the right and left are set to the second.</li>
21926      * <li>If there are three values, the top is set to the first value, the left
21927      * and right are set to the second, and the bottom is set to the third.</li>
21928      * <li>If there are four values, they apply to the top, right, bottom, and
21929      * left, respectively.</li>
21930      * </ul></div>
21931      * <p>Defaults to:</p><pre><code>
21932      * {top:0, right:0, bottom:0, left:0}
21933      * </code></pre>
21934      */
21935     defaultMargins : {left:0,top:0,right:0,bottom:0},
21936     /**
21937      * @cfg {String} padding
21938      * <p>Sets the padding to be applied to all child items managed by this layout.</p>
21939      * <p>This property must be specified as a string containing
21940      * space-separated, numeric padding values. The order of the sides associated
21941      * with each value matches the way CSS processes padding values:</p>
21942      * <div class="mdetail-params"><ul>
21943      * <li>If there is only one value, it applies to all sides.</li>
21944      * <li>If there are two values, the top and bottom borders are set to the
21945      * first value and the right and left are set to the second.</li>
21946      * <li>If there are three values, the top is set to the first value, the left
21947      * and right are set to the second, and the bottom is set to the third.</li>
21948      * <li>If there are four values, they apply to the top, right, bottom, and
21949      * left, respectively.</li>
21950      * </ul></div>
21951      * <p>Defaults to: <code>"0"</code></p>
21952      */
21953     padding : '0',
21954     // documented in subclasses
21955     pack : 'start',
21956
21957     // private
21958     monitorResize : true,
21959     type: 'box',
21960     scrollOffset : 0,
21961     extraCls : 'x-box-item',
21962     targetCls : 'x-box-layout-ct',
21963     innerCls : 'x-box-inner',
21964
21965     constructor : function(config){
21966         Ext.layout.BoxLayout.superclass.constructor.call(this, config);
21967         if(Ext.isString(this.defaultMargins)){
21968             this.defaultMargins = this.parseMargins(this.defaultMargins);
21969         }
21970     },
21971
21972     // private
21973     isValidParent : function(c, target){
21974         return this.innerCt && c.getPositionEl().dom.parentNode == this.innerCt.dom;
21975     },
21976
21977     // private
21978     renderAll : function(ct, target){
21979         if(!this.innerCt){
21980             // the innerCt prevents wrapping and shuffling while
21981             // the container is resizing
21982             this.innerCt = target.createChild({cls:this.innerCls});
21983             this.padding = this.parseMargins(this.padding);
21984         }
21985         Ext.layout.BoxLayout.superclass.renderAll.call(this, ct, this.innerCt);
21986     },
21987
21988     onLayout : function(ct, target){
21989         this.renderAll(ct, target);
21990     },
21991
21992     getLayoutTargetSize : function(){
21993         var target = this.container.getLayoutTarget(), ret;
21994         if (target) {
21995             ret = target.getViewSize();
21996             ret.width -= target.getPadding('lr');
21997             ret.height -= target.getPadding('tb');
21998         }
21999         return ret;
22000     },
22001
22002     // private
22003     renderItem : function(c){
22004         if(Ext.isString(c.margins)){
22005             c.margins = this.parseMargins(c.margins);
22006         }else if(!c.margins){
22007             c.margins = this.defaultMargins;
22008         }
22009         Ext.layout.BoxLayout.superclass.renderItem.apply(this, arguments);
22010     }
22011 });
22012
22013 /**
22014  * @class Ext.layout.VBoxLayout
22015  * @extends Ext.layout.BoxLayout
22016  * <p>A layout that arranges items vertically down a Container. This layout optionally divides available vertical
22017  * space between child items containing a numeric <code>flex</code> configuration.</p>
22018  * This layout may also be used to set the widths of child items by configuring it with the {@link #align} option.
22019  */
22020 Ext.layout.VBoxLayout = Ext.extend(Ext.layout.BoxLayout, {
22021     /**
22022      * @cfg {String} align
22023      * Controls how the child items of the container are aligned. Acceptable configuration values for this
22024      * property are:
22025      * <div class="mdetail-params"><ul>
22026      * <li><b><tt>left</tt></b> : <b>Default</b><div class="sub-desc">child items are aligned horizontally
22027      * at the <b>left</b> side of the container</div></li>
22028      * <li><b><tt>center</tt></b> : <div class="sub-desc">child items are aligned horizontally at the
22029      * <b>mid-width</b> of the container</div></li>
22030      * <li><b><tt>stretch</tt></b> : <div class="sub-desc">child items are stretched horizontally to fill
22031      * the width of the container</div></li>
22032      * <li><b><tt>stretchmax</tt></b> : <div class="sub-desc">child items are stretched horizontally to
22033      * the size of the largest item.</div></li>
22034      * </ul></div>
22035      */
22036     align : 'left', // left, center, stretch, strechmax
22037     type: 'vbox',
22038     /**
22039      * @cfg {String} pack
22040      * Controls how the child items of the container are packed together. Acceptable configuration values
22041      * for this property are:
22042      * <div class="mdetail-params"><ul>
22043      * <li><b><tt>start</tt></b> : <b>Default</b><div class="sub-desc">child items are packed together at
22044      * <b>top</b> side of container</div></li>
22045      * <li><b><tt>center</tt></b> : <div class="sub-desc">child items are packed together at
22046      * <b>mid-height</b> of container</div></li>
22047      * <li><b><tt>end</tt></b> : <div class="sub-desc">child items are packed together at <b>bottom</b>
22048      * side of container</div></li>
22049      * </ul></div>
22050      */
22051     /**
22052      * @cfg {Number} flex
22053      * This configuation option is to be applied to <b>child <tt>items</tt></b> of the container managed
22054      * by this layout. Each child item with a <tt>flex</tt> property will be flexed <b>vertically</b>
22055      * according to each item's <b>relative</b> <tt>flex</tt> value compared to the sum of all items with
22056      * a <tt>flex</tt> value specified.  Any child items that have either a <tt>flex = 0</tt> or
22057      * <tt>flex = undefined</tt> will not be 'flexed' (the initial size will not be changed).
22058      */
22059
22060     // private
22061     onLayout : function(ct, target){
22062         Ext.layout.VBoxLayout.superclass.onLayout.call(this, ct, target);
22063
22064         var cs = this.getRenderedItems(ct), csLen = cs.length,
22065             c, i, cm, ch, margin, cl, diff, aw, availHeight,
22066             size = this.getLayoutTargetSize(),
22067             w = size.width,
22068             h = size.height - this.scrollOffset,
22069             l = this.padding.left,
22070             t = this.padding.top,
22071             isStart = this.pack == 'start',
22072             extraHeight = 0,
22073             maxWidth = 0,
22074             totalFlex = 0,
22075             usedHeight = 0,
22076             idx = 0,
22077             heights = [],
22078             restore = [];
22079
22080         // Do only width calculations and apply those first, as they can affect height
22081         for (i = 0 ; i < csLen; i++) {
22082             c = cs[i];
22083             cm = c.margins;
22084             margin = cm.top + cm.bottom;
22085             // Max height for align
22086             maxWidth = Math.max(maxWidth, c.getWidth() + cm.left + cm.right);
22087         }
22088
22089         var innerCtWidth = maxWidth + this.padding.left + this.padding.right;
22090         switch(this.align){
22091             case 'stretch':
22092                 this.innerCt.setSize(w, h);
22093                 break;
22094             case 'stretchmax':
22095             case 'left':
22096                 this.innerCt.setSize(innerCtWidth, h);
22097                 break;
22098             case 'center':
22099                 this.innerCt.setSize(w = Math.max(w, innerCtWidth), h);
22100                 break;
22101         }
22102
22103         var availableWidth = Math.max(0, w - this.padding.left - this.padding.right);
22104         // Apply widths
22105         for (i = 0 ; i < csLen; i++) {
22106             c = cs[i];
22107             cm = c.margins;
22108             if(this.align == 'stretch'){
22109                 c.setWidth(((w - (this.padding.left + this.padding.right)) - (cm.left + cm.right)).constrain(
22110                     c.minWidth || 0, c.maxWidth || 1000000));
22111             }else if(this.align == 'stretchmax'){
22112                 c.setWidth((maxWidth - (cm.left + cm.right)).constrain(
22113                     c.minWidth || 0, c.maxWidth || 1000000));
22114             }else if(isStart && c.flex){
22115                 c.setWidth();
22116             }
22117
22118         }
22119
22120         // Height calculations
22121         for (i = 0 ; i < csLen; i++) {
22122             c = cs[i];
22123             // Total of all the flex values
22124             totalFlex += c.flex || 0;
22125             // Don't run height calculations on flexed items
22126             if (!c.flex) {
22127                 // Render and layout sub-containers without a flex or height, once
22128                 if (!c.height && !c.hasLayout && c.doLayout) {
22129                     c.doLayout();
22130                 }
22131                 ch = c.getHeight();
22132             } else {
22133                 ch = 0;
22134             }
22135
22136             cm = c.margins;
22137             // Determine how much height is available to flex
22138             extraHeight += ch + cm.top + cm.bottom;
22139         }
22140         // Final avail height calc
22141         availHeight = Math.max(0, (h - extraHeight - this.padding.top - this.padding.bottom));
22142
22143         var leftOver = availHeight;
22144         for (i = 0 ; i < csLen; i++) {
22145             c = cs[i];
22146             if(isStart && c.flex){
22147                 ch = Math.floor(availHeight * (c.flex / totalFlex));
22148                 leftOver -= ch;
22149                 heights.push(ch);
22150             }
22151         }
22152         if(this.pack == 'center'){
22153             t += availHeight ? availHeight / 2 : 0;
22154         }else if(this.pack == 'end'){
22155             t += availHeight;
22156         }
22157         idx = 0;
22158         // Apply heights
22159         for (i = 0 ; i < csLen; i++) {
22160             c = cs[i];
22161             cm = c.margins;
22162             t += cm.top;
22163             aw = availableWidth;
22164             cl = l + cm.left // default left pos
22165
22166             // Adjust left pos for centering
22167             if(this.align == 'center'){
22168                 if((diff = availableWidth - (c.getWidth() + cm.left + cm.right)) > 0){
22169                     cl += (diff/2);
22170                     aw -= diff;
22171                 }
22172             }
22173
22174             c.setPosition(cl, t);
22175             if(isStart && c.flex){
22176                 ch = Math.max(0, heights[idx++] + (leftOver-- > 0 ? 1 : 0));
22177                 c.setSize(aw, ch);
22178             }else{
22179                 ch = c.getHeight();
22180             }
22181             t += ch + cm.bottom;
22182         }
22183         // Putting a box layout into an overflowed container is NOT correct and will make a second layout pass necessary.
22184         if (i = target.getStyle('overflow') && i != 'hidden' && !this.adjustmentPass) {
22185             var ts = this.getLayoutTargetSize();
22186             if (ts.width != size.width || ts.height != size.height){
22187                 this.adjustmentPass = true;
22188                 this.onLayout(ct, target);
22189             }
22190         }
22191         delete this.adjustmentPass;
22192     }
22193 });
22194
22195 Ext.Container.LAYOUTS.vbox = Ext.layout.VBoxLayout;
22196
22197 /**
22198  * @class Ext.layout.HBoxLayout
22199  * @extends Ext.layout.BoxLayout
22200  * <p>A layout that arranges items horizontally across a Container. This layout optionally divides available horizontal
22201  * space between child items containing a numeric <code>flex</code> configuration.</p>
22202  * This layout may also be used to set the heights of child items by configuring it with the {@link #align} option.
22203  */
22204 Ext.layout.HBoxLayout = Ext.extend(Ext.layout.BoxLayout, {
22205     /**
22206      * @cfg {String} align
22207      * Controls how the child items of the container are aligned. Acceptable configuration values for this
22208      * property are:
22209      * <div class="mdetail-params"><ul>
22210      * <li><b><tt>top</tt></b> : <b>Default</b><div class="sub-desc">child items are aligned vertically
22211      * at the <b>top</b> of the container</div></li>
22212      * <li><b><tt>middle</tt></b> : <div class="sub-desc">child items are aligned vertically in the
22213      * <b>middle</b> of the container</div></li>
22214      * <li><b><tt>stretch</tt></b> : <div class="sub-desc">child items are stretched vertically to fill
22215      * the height of the container</div></li>
22216      * <li><b><tt>stretchmax</tt></b> : <div class="sub-desc">child items are stretched vertically to
22217      * the height of the largest item.</div></li>
22218      */
22219     align : 'top', // top, middle, stretch, strechmax
22220     type: 'hbox',
22221     /**
22222      * @cfg {String} pack
22223      * Controls how the child items of the container are packed together. Acceptable configuration values
22224      * for this property are:
22225      * <div class="mdetail-params"><ul>
22226      * <li><b><tt>start</tt></b> : <b>Default</b><div class="sub-desc">child items are packed together at
22227      * <b>left</b> side of container</div></li>
22228      * <li><b><tt>center</tt></b> : <div class="sub-desc">child items are packed together at
22229      * <b>mid-width</b> of container</div></li>
22230      * <li><b><tt>end</tt></b> : <div class="sub-desc">child items are packed together at <b>right</b>
22231      * side of container</div></li>
22232      * </ul></div>
22233      */
22234     /**
22235      * @cfg {Number} flex
22236      * This configuation option is to be applied to <b>child <tt>items</tt></b> of the container managed
22237      * by this layout. Each child item with a <tt>flex</tt> property will be flexed <b>horizontally</b>
22238      * according to each item's <b>relative</b> <tt>flex</tt> value compared to the sum of all items with
22239      * a <tt>flex</tt> value specified.  Any child items that have either a <tt>flex = 0</tt> or
22240      * <tt>flex = undefined</tt> will not be 'flexed' (the initial size will not be changed).
22241      */
22242
22243     // private
22244     onLayout : function(ct, target){
22245         Ext.layout.HBoxLayout.superclass.onLayout.call(this, ct, target);
22246
22247         var cs = this.getRenderedItems(ct), csLen = cs.length,
22248             c, i, cm, cw, ch, diff, availWidth,
22249             size = this.getLayoutTargetSize(),
22250             w = size.width - this.scrollOffset,
22251             h = size.height,
22252             l = this.padding.left,
22253             t = this.padding.top,
22254             isStart = this.pack == 'start',
22255             isRestore = ['stretch', 'stretchmax'].indexOf(this.align) == -1,
22256             extraWidth = 0,
22257             maxHeight = 0,
22258             totalFlex = 0,
22259             usedWidth = 0;
22260
22261         for (i = 0 ; i < csLen; i++) {
22262             c = cs[i];
22263             // Total of all the flex values
22264             totalFlex += c.flex || 0;
22265             // Don't run width calculations on flexed items
22266             if (!c.flex) {
22267                 // Render and layout sub-containers without a flex or width, once
22268                 if (!c.width && !c.hasLayout && c.doLayout) {
22269                     c.doLayout();
22270                 }
22271                 cw = c.getWidth();
22272             } else {
22273                 cw = 0;
22274             }
22275             cm = c.margins;
22276             // Determine how much width is available to flex
22277             extraWidth += cw + cm.left + cm.right;
22278             // Max height for align
22279             maxHeight = Math.max(maxHeight, c.getHeight() + cm.top + cm.bottom);
22280         }
22281         // Final avail width calc
22282         availWidth = Math.max(0, (w - extraWidth - this.padding.left - this.padding.right));
22283
22284         var innerCtHeight = maxHeight + this.padding.top + this.padding.bottom;
22285         switch(this.align){
22286             case 'stretch':
22287                 this.innerCt.setSize(w, h);
22288                 break;
22289             case 'stretchmax':
22290             case 'top':
22291                 this.innerCt.setSize(w, innerCtHeight);
22292                 break;
22293             case 'middle':
22294                 this.innerCt.setSize(w, h = Math.max(h, innerCtHeight));
22295                 break;
22296         }
22297
22298         var leftOver = availWidth,
22299             widths = [],
22300             restore = [],
22301             idx = 0,
22302             availableHeight = Math.max(0, h - this.padding.top - this.padding.bottom);
22303
22304         for (i = 0 ; i < csLen; i++) {
22305             c = cs[i];
22306             if(isStart && c.flex){
22307                 cw = Math.floor(availWidth * (c.flex / totalFlex));
22308                 leftOver -= cw;
22309                 widths.push(cw);
22310             }
22311         }
22312
22313         if(this.pack == 'center'){
22314             l += availWidth ? availWidth / 2 : 0;
22315         }else if(this.pack == 'end'){
22316             l += availWidth;
22317         }
22318         for (i = 0 ; i < csLen; i++) {
22319             c = cs[i];
22320             cm = c.margins;
22321             l += cm.left;
22322             c.setPosition(l, t + cm.top);
22323             if(isStart && c.flex){
22324                 cw = Math.max(0, widths[idx++] + (leftOver-- > 0 ? 1 : 0));
22325                 if(isRestore){
22326                     restore.push(c.getHeight());
22327                 }
22328                 c.setSize(cw, availableHeight);
22329             }else{
22330                 cw = c.getWidth();
22331             }
22332             l += cw + cm.right;
22333         }
22334
22335         idx = 0;
22336         for (i = 0 ; i < csLen; i++) {
22337             c = cs[i];
22338             cm = c.margins;
22339             ch = c.getHeight();
22340             if(isStart && c.flex){
22341                 ch = restore[idx++];
22342             }
22343             if(this.align == 'stretch'){
22344                 c.setHeight(((h - (this.padding.top + this.padding.bottom)) - (cm.top + cm.bottom)).constrain(
22345                     c.minHeight || 0, c.maxHeight || 1000000));
22346             }else if(this.align == 'stretchmax'){
22347                 c.setHeight((maxHeight - (cm.top + cm.bottom)).constrain(
22348                     c.minHeight || 0, c.maxHeight || 1000000));
22349             }else{
22350                 if(this.align == 'middle'){
22351                     diff = availableHeight - (ch + cm.top + cm.bottom);
22352                     ch = t + cm.top + (diff/2);
22353                     if(diff > 0){
22354                         c.setPosition(c.x, ch);
22355                     }
22356                 }
22357                 if(isStart && c.flex){
22358                     c.setHeight(ch);
22359                 }
22360             }
22361         }
22362         // Putting a box layout into an overflowed container is NOT correct and will make a second layout pass necessary.
22363         if (i = target.getStyle('overflow') && i != 'hidden' && !this.adjustmentPass) {
22364             var ts = this.getLayoutTargetSize();
22365             if (ts.width != size.width || ts.height != size.height){
22366                 this.adjustmentPass = true;
22367                 this.onLayout(ct, target);
22368             }
22369         }
22370         delete this.adjustmentPass;
22371     }
22372 });
22373
22374 Ext.Container.LAYOUTS.hbox = Ext.layout.HBoxLayout;
22375 /**
22376  * @class Ext.layout.ToolbarLayout
22377  * @extends Ext.layout.ContainerLayout
22378  * Layout manager implicitly used by Ext.Toolbar.
22379  */
22380 Ext.layout.ToolbarLayout = Ext.extend(Ext.layout.ContainerLayout, {
22381     monitorResize : true,
22382     triggerWidth : 18,
22383     lastOverflow : false,
22384
22385     noItemsMenuText : '<div class="x-toolbar-no-items">(None)</div>',
22386
22387     // private
22388     onLayout : function(ct, target){
22389         if(!this.leftTr){
22390             var align = ct.buttonAlign == 'center' ? 'center' : 'left';
22391             target.addClass('x-toolbar-layout-ct');
22392             target.insertHtml('beforeEnd',
22393                  '<table cellspacing="0" class="x-toolbar-ct"><tbody><tr><td class="x-toolbar-left" align="' + align + '"><table cellspacing="0"><tbody><tr class="x-toolbar-left-row"></tr></tbody></table></td><td class="x-toolbar-right" align="right"><table cellspacing="0" class="x-toolbar-right-ct"><tbody><tr><td><table cellspacing="0"><tbody><tr class="x-toolbar-right-row"></tr></tbody></table></td><td><table cellspacing="0"><tbody><tr class="x-toolbar-extras-row"></tr></tbody></table></td></tr></tbody></table></td></tr></tbody></table>');
22394             this.leftTr = target.child('tr.x-toolbar-left-row', true);
22395             this.rightTr = target.child('tr.x-toolbar-right-row', true);
22396             this.extrasTr = target.child('tr.x-toolbar-extras-row', true);
22397         }
22398
22399         var side = ct.buttonAlign == 'right' ? this.rightTr : this.leftTr,
22400             pos = 0,
22401             items = ct.items.items;
22402
22403         for(var i = 0, len = items.length, c; i < len; i++, pos++) {
22404             c = items[i];
22405             if(c.isFill){
22406                 side = this.rightTr;
22407                 pos = -1;
22408             }else if(!c.rendered){
22409                 c.render(this.insertCell(c, side, pos));
22410             }else{
22411                 if(!c.xtbHidden && !this.isValidParent(c, side.childNodes[pos])){
22412                     var td = this.insertCell(c, side, pos);
22413                     td.appendChild(c.getPositionEl().dom);
22414                     c.container = Ext.get(td);
22415                 }
22416             }
22417         }
22418         //strip extra empty cells
22419         this.cleanup(this.leftTr);
22420         this.cleanup(this.rightTr);
22421         this.cleanup(this.extrasTr);
22422         this.fitToSize(target);
22423     },
22424
22425     cleanup : function(row){
22426         var cn = row.childNodes, i, c;
22427         for(i = cn.length-1; i >= 0 && (c = cn[i]); i--){
22428             if(!c.firstChild){
22429                 row.removeChild(c);
22430             }
22431         }
22432     },
22433
22434     insertCell : function(c, side, pos){
22435         var td = document.createElement('td');
22436         td.className='x-toolbar-cell';
22437         side.insertBefore(td, side.childNodes[pos]||null);
22438         return td;
22439     },
22440
22441     hideItem : function(item){
22442         var h = (this.hiddens = this.hiddens || []);
22443         h.push(item);
22444         item.xtbHidden = true;
22445         item.xtbWidth = item.getPositionEl().dom.parentNode.offsetWidth;
22446         item.hide();
22447     },
22448
22449     unhideItem : function(item){
22450         item.show();
22451         item.xtbHidden = false;
22452         this.hiddens.remove(item);
22453         if(this.hiddens.length < 1){
22454             delete this.hiddens;
22455         }
22456     },
22457
22458     getItemWidth : function(c){
22459         return c.hidden ? (c.xtbWidth || 0) : c.getPositionEl().dom.parentNode.offsetWidth;
22460     },
22461
22462     fitToSize : function(t){
22463         if(this.container.enableOverflow === false){
22464             return;
22465         }
22466         var w = t.dom.clientWidth,
22467             lw = this.lastWidth || 0,
22468             iw = t.dom.firstChild.offsetWidth,
22469             clipWidth = w - this.triggerWidth,
22470             hideIndex = -1;
22471
22472         this.lastWidth = w;
22473
22474         if(iw > w || (this.hiddens && w >= lw)){
22475             var i, items = this.container.items.items,
22476                 len = items.length, c,
22477                 loopWidth = 0;
22478
22479             for(i = 0; i < len; i++) {
22480                 c = items[i];
22481                 if(!c.isFill){
22482                     loopWidth += this.getItemWidth(c);
22483                     if(loopWidth > clipWidth){
22484                         if(!(c.hidden || c.xtbHidden)){
22485                             this.hideItem(c);
22486                         }
22487                     }else if(c.xtbHidden){
22488                         this.unhideItem(c);
22489                     }
22490                 }
22491             }
22492         }
22493         if(this.hiddens){
22494             this.initMore();
22495             if(!this.lastOverflow){
22496                 this.container.fireEvent('overflowchange', this.container, true);
22497                 this.lastOverflow = true;
22498             }
22499         }else if(this.more){
22500             this.clearMenu();
22501             this.more.destroy();
22502             delete this.more;
22503             if(this.lastOverflow){
22504                 this.container.fireEvent('overflowchange', this.container, false);
22505                 this.lastOverflow = false;
22506             }
22507         }
22508     },
22509
22510     createMenuConfig : function(c, hideOnClick){
22511         var cfg = Ext.apply({}, c.initialConfig),
22512             group = c.toggleGroup;
22513
22514         Ext.apply(cfg, {
22515             text: c.overflowText || c.text,
22516             iconCls: c.iconCls,
22517             icon: c.icon,
22518             itemId: c.itemId,
22519             disabled: c.disabled,
22520             handler: c.handler,
22521             scope: c.scope,
22522             menu: c.menu,
22523             hideOnClick: hideOnClick
22524         });
22525         if(group || c.enableToggle){
22526             Ext.apply(cfg, {
22527                 group: group,
22528                 checked: c.pressed,
22529                 listeners: {
22530                     checkchange: function(item, checked){
22531                         c.toggle(checked);
22532                     }
22533                 }
22534             });
22535         }
22536         delete cfg.ownerCt;
22537         delete cfg.xtype;
22538         delete cfg.id;
22539         return cfg;
22540     },
22541
22542     // private
22543     addComponentToMenu : function(m, c){
22544         if(c instanceof Ext.Toolbar.Separator){
22545             m.add('-');
22546         }else if(Ext.isFunction(c.isXType)){
22547             if(c.isXType('splitbutton')){
22548                 m.add(this.createMenuConfig(c, true));
22549             }else if(c.isXType('button')){
22550                 m.add(this.createMenuConfig(c, !c.menu));
22551             }else if(c.isXType('buttongroup')){
22552                 c.items.each(function(item){
22553                      this.addComponentToMenu(m, item);
22554                 }, this);
22555             }
22556         }
22557     },
22558
22559     clearMenu : function(){
22560         var m = this.moreMenu;
22561         if(m && m.items){
22562             m.items.each(function(item){
22563                 delete item.menu;
22564             });
22565         }
22566     },
22567
22568     // private
22569     beforeMoreShow : function(m){
22570         var h = this.container.items.items,
22571             len = h.length,
22572             c,
22573             prev,
22574             needsSep = function(group, item){
22575                 return group.isXType('buttongroup') && !(item instanceof Ext.Toolbar.Separator);
22576             };
22577
22578         this.clearMenu();
22579         m.removeAll();
22580         for(var i = 0; i < len; i++){
22581             c = h[i];
22582             if(c.xtbHidden){
22583                 if(prev && (needsSep(c, prev) || needsSep(prev, c))){
22584                     m.add('-');
22585                 }
22586                 this.addComponentToMenu(m, c);
22587                 prev = c;
22588             }
22589         }
22590         // put something so the menu isn't empty
22591         // if no compatible items found
22592         if(m.items.length < 1){
22593             m.add(this.noItemsMenuText);
22594         }
22595     },
22596
22597     initMore : function(){
22598         if(!this.more){
22599             this.moreMenu = new Ext.menu.Menu({
22600                 ownerCt : this.container,
22601                 listeners: {
22602                     beforeshow: this.beforeMoreShow,
22603                     scope: this
22604                 }
22605
22606             });
22607             this.more = new Ext.Button({
22608                 iconCls : 'x-toolbar-more-icon',
22609                 cls     : 'x-toolbar-more',
22610                 menu    : this.moreMenu,
22611                 ownerCt : this.container
22612             });
22613             var td = this.insertCell(this.more, this.extrasTr, 100);
22614             this.more.render(td);
22615         }
22616     },
22617
22618     destroy : function(){
22619         Ext.destroy(this.more, this.moreMenu);
22620         delete this.leftTr;
22621         delete this.rightTr;
22622         delete this.extrasTr;
22623         Ext.layout.ToolbarLayout.superclass.destroy.call(this);
22624     }
22625 });
22626
22627 Ext.Container.LAYOUTS.toolbar = Ext.layout.ToolbarLayout;/**
22628  * @class Ext.layout.MenuLayout
22629  * @extends Ext.layout.ContainerLayout
22630  * <p>Layout manager used by {@link Ext.menu.Menu}. Generally this class should not need to be used directly.</p>
22631  */
22632  Ext.layout.MenuLayout = Ext.extend(Ext.layout.ContainerLayout, {
22633     monitorResize : true,
22634
22635     setContainer : function(ct){
22636         this.monitorResize = !ct.floating;
22637         // This event is only fired by the menu in IE, used so we don't couple
22638         // the menu with the layout.
22639         ct.on('autosize', this.doAutoSize, this);
22640         Ext.layout.MenuLayout.superclass.setContainer.call(this, ct);
22641     },
22642
22643     renderItem : function(c, position, target){
22644         if (!this.itemTpl) {
22645             this.itemTpl = Ext.layout.MenuLayout.prototype.itemTpl = new Ext.XTemplate(
22646                 '<li id="{itemId}" class="{itemCls}">',
22647                     '<tpl if="needsIcon">',
22648                         '<img src="{icon}" class="{iconCls}"/>',
22649                     '</tpl>',
22650                 '</li>'
22651             );
22652         }
22653
22654         if(c && !c.rendered){
22655             if(Ext.isNumber(position)){
22656                 position = target.dom.childNodes[position];
22657             }
22658             var a = this.getItemArgs(c);
22659
22660 //          The Component's positionEl is the <li> it is rendered into
22661             c.render(c.positionEl = position ?
22662                 this.itemTpl.insertBefore(position, a, true) :
22663                 this.itemTpl.append(target, a, true));
22664
22665 //          Link the containing <li> to the item.
22666             c.positionEl.menuItemId = c.getItemId();
22667
22668 //          If rendering a regular Component, and it needs an icon,
22669 //          move the Component rightwards.
22670             if (!a.isMenuItem && a.needsIcon) {
22671                 c.positionEl.addClass('x-menu-list-item-indent');
22672             }
22673             this.configureItem(c, position);
22674         }else if(c && !this.isValidParent(c, target)){
22675             if(Ext.isNumber(position)){
22676                 position = target.dom.childNodes[position];
22677             }
22678             target.dom.insertBefore(c.getActionEl().dom, position || null);
22679         }
22680     },
22681
22682     getItemArgs : function(c) {
22683         var isMenuItem = c instanceof Ext.menu.Item;
22684         return {
22685             isMenuItem: isMenuItem,
22686             needsIcon: !isMenuItem && (c.icon || c.iconCls),
22687             icon: c.icon || Ext.BLANK_IMAGE_URL,
22688             iconCls: 'x-menu-item-icon ' + (c.iconCls || ''),
22689             itemId: 'x-menu-el-' + c.id,
22690             itemCls: 'x-menu-list-item '
22691         };
22692     },
22693
22694     //  Valid if the Component is in a <li> which is part of our target <ul>
22695     isValidParent : function(c, target) {
22696         return c.el.up('li.x-menu-list-item', 5).dom.parentNode === (target.dom || target);
22697     },
22698
22699     onLayout : function(ct, target){
22700         Ext.layout.MenuLayout.superclass.onLayout.call(this, ct, target);
22701         this.doAutoSize();
22702     },
22703
22704     doAutoSize : function(){
22705         var ct = this.container, w = ct.width;
22706         if(ct.floating){
22707             if(w){
22708                 ct.setWidth(w);
22709             }else if(Ext.isIE){
22710                 ct.setWidth(Ext.isStrict && (Ext.isIE7 || Ext.isIE8) ? 'auto' : ct.minWidth);
22711                 var el = ct.getEl(), t = el.dom.offsetWidth; // force recalc
22712                 ct.setWidth(ct.getLayoutTarget().getWidth() + el.getFrameWidth('lr'));
22713             }
22714         }
22715     }
22716 });
22717 Ext.Container.LAYOUTS['menu'] = Ext.layout.MenuLayout;/**\r
22718  * @class Ext.Viewport\r
22719  * @extends Ext.Container\r
22720  * <p>A specialized container representing the viewable application area (the browser viewport).</p>\r
22721  * <p>The Viewport renders itself to the document body, and automatically sizes itself to the size of\r
22722  * the browser viewport and manages window resizing. There may only be one Viewport created\r
22723  * in a page. Inner layouts are available by virtue of the fact that all {@link Ext.Panel Panel}s\r
22724  * added to the Viewport, either through its {@link #items}, or through the items, or the {@link #add}\r
22725  * method of any of its child Panels may themselves have a layout.</p>\r
22726  * <p>The Viewport does not provide scrolling, so child Panels within the Viewport should provide\r
22727  * for scrolling if needed using the {@link #autoScroll} config.</p>\r
22728  * <p>An example showing a classic application border layout:</p><pre><code>\r
22729 new Ext.Viewport({\r
22730     layout: 'border',\r
22731     items: [{\r
22732         region: 'north',\r
22733         html: '&lt;h1 class="x-panel-header">Page Title&lt;/h1>',\r
22734         autoHeight: true,\r
22735         border: false,\r
22736         margins: '0 0 5 0'\r
22737     }, {\r
22738         region: 'west',\r
22739         collapsible: true,\r
22740         title: 'Navigation',\r
22741         width: 200\r
22742         // the west region might typically utilize a {@link Ext.tree.TreePanel TreePanel} or a Panel with {@link Ext.layout.AccordionLayout Accordion layout}\r
22743     }, {\r
22744         region: 'south',\r
22745         title: 'Title for Panel',\r
22746         collapsible: true,\r
22747         html: 'Information goes here',\r
22748         split: true,\r
22749         height: 100,\r
22750         minHeight: 100\r
22751     }, {\r
22752         region: 'east',\r
22753         title: 'Title for the Grid Panel',\r
22754         collapsible: true,\r
22755         split: true,\r
22756         width: 200,\r
22757         xtype: 'grid',\r
22758         // remaining grid configuration not shown ...\r
22759         // notice that the GridPanel is added directly as the region\r
22760         // it is not "overnested" inside another Panel\r
22761     }, {\r
22762         region: 'center',\r
22763         xtype: 'tabpanel', // TabPanel itself has no title\r
22764         items: {\r
22765             title: 'Default Tab',\r
22766             html: 'The first tab\'s content. Others may be added dynamically'\r
22767         }\r
22768     }]\r
22769 });\r
22770 </code></pre>\r
22771  * @constructor\r
22772  * Create a new Viewport\r
22773  * @param {Object} config The config object\r
22774  * @xtype viewport\r
22775  */\r
22776 Ext.Viewport = Ext.extend(Ext.Container, {\r
22777     /*\r
22778      * Privatize config options which, if used, would interfere with the\r
22779      * correct operation of the Viewport as the sole manager of the\r
22780      * layout of the document body.\r
22781      */\r
22782     /**\r
22783      * @cfg {Mixed} applyTo @hide\r
22784      */\r
22785     /**\r
22786      * @cfg {Boolean} allowDomMove @hide\r
22787      */\r
22788     /**\r
22789      * @cfg {Boolean} hideParent @hide\r
22790      */\r
22791     /**\r
22792      * @cfg {Mixed} renderTo @hide\r
22793      */\r
22794     /**\r
22795      * @cfg {Boolean} hideParent @hide\r
22796      */\r
22797     /**\r
22798      * @cfg {Number} height @hide\r
22799      */\r
22800     /**\r
22801      * @cfg {Number} width @hide\r
22802      */\r
22803     /**\r
22804      * @cfg {Boolean} autoHeight @hide\r
22805      */\r
22806     /**\r
22807      * @cfg {Boolean} autoWidth @hide\r
22808      */\r
22809     /**\r
22810      * @cfg {Boolean} deferHeight @hide\r
22811      */\r
22812     /**\r
22813      * @cfg {Boolean} monitorResize @hide\r
22814      */\r
22815 \r
22816     initComponent : function() {\r
22817         Ext.Viewport.superclass.initComponent.call(this);\r
22818         document.getElementsByTagName('html')[0].className += ' x-viewport';\r
22819         this.el = Ext.getBody();\r
22820         this.el.setHeight = Ext.emptyFn;\r
22821         this.el.setWidth = Ext.emptyFn;\r
22822         this.el.setSize = Ext.emptyFn;\r
22823         this.el.dom.scroll = 'no';\r
22824         this.allowDomMove = false;\r
22825         this.autoWidth = true;\r
22826         this.autoHeight = true;\r
22827         Ext.EventManager.onWindowResize(this.fireResize, this);\r
22828         this.renderTo = this.el;\r
22829     },\r
22830 \r
22831     fireResize : function(w, h){\r
22832         this.fireEvent('resize', this, w, h, w, h);\r
22833     }\r
22834 });\r
22835 Ext.reg('viewport', Ext.Viewport);\r
22836 /**
22837  * @class Ext.Panel
22838  * @extends Ext.Container
22839  * <p>Panel is a container that has specific functionality and structural components that make
22840  * it the perfect building block for application-oriented user interfaces.</p>
22841  * <p>Panels are, by virtue of their inheritance from {@link Ext.Container}, capable
22842  * of being configured with a {@link Ext.Container#layout layout}, and containing child Components.</p>
22843  * <p>When either specifying child {@link Ext.Component#items items} of a Panel, or dynamically {@link Ext.Container#add adding} Components
22844  * to a Panel, remember to consider how you wish the Panel to arrange those child elements, and whether
22845  * those child elements need to be sized using one of Ext's built-in <code><b>{@link Ext.Container#layout layout}</b></code> schemes. By
22846  * default, Panels use the {@link Ext.layout.ContainerLayout ContainerLayout} scheme. This simply renders
22847  * child components, appending them one after the other inside the Container, and <b>does not apply any sizing</b>
22848  * at all.</p>
22849  * <p>A Panel may also contain {@link #bbar bottom} and {@link #tbar top} toolbars, along with separate
22850  * {@link #header}, {@link #footer} and {@link #body} sections (see {@link #frame} for additional
22851  * information).</p>
22852  * <p>Panel also provides built-in {@link #collapsible expandable and collapsible behavior}, along with
22853  * a variety of {@link #tools prebuilt tool buttons} that can be wired up to provide other customized
22854  * behavior.  Panels can be easily dropped into any {@link Ext.Container Container} or layout, and the
22855  * layout and rendering pipeline is {@link Ext.Container#add completely managed by the framework}.</p>
22856  * @constructor
22857  * @param {Object} config The config object
22858  * @xtype panel
22859  */
22860 Ext.Panel = Ext.extend(Ext.Container, {
22861     /**
22862      * The Panel's header {@link Ext.Element Element}. Read-only.
22863      * <p>This Element is used to house the {@link #title} and {@link #tools}</p>
22864      * <br><p><b>Note</b>: see the Note for <code>{@link Ext.Component#el el}</code> also.</p>
22865      * @type Ext.Element
22866      * @property header
22867      */
22868     /**
22869      * The Panel's body {@link Ext.Element Element} which may be used to contain HTML content.
22870      * The content may be specified in the {@link #html} config, or it may be loaded using the
22871      * {@link autoLoad} config, or through the Panel's {@link #getUpdater Updater}. Read-only.
22872      * <p>If this is used to load visible HTML elements in either way, then
22873      * the Panel may not be used as a Layout for hosting nested Panels.</p>
22874      * <p>If this Panel is intended to be used as the host of a Layout (See {@link #layout}
22875      * then the body Element must not be loaded or changed - it is under the control
22876      * of the Panel's Layout.
22877      * <br><p><b>Note</b>: see the Note for <code>{@link Ext.Component#el el}</code> also.</p>
22878      * @type Ext.Element
22879      * @property body
22880      */
22881     /**
22882      * The Panel's bwrap {@link Ext.Element Element} used to contain other Panel elements
22883      * (tbar, body, bbar, footer). See {@link #bodyCfg}. Read-only.
22884      * @type Ext.Element
22885      * @property bwrap
22886      */
22887     /**
22888      * True if this panel is collapsed. Read-only.
22889      * @type Boolean
22890      * @property collapsed
22891      */
22892     /**
22893      * @cfg {Object} bodyCfg
22894      * <p>A {@link Ext.DomHelper DomHelper} element specification object may be specified for any
22895      * Panel Element.</p>
22896      * <p>By default, the Default element in the table below will be used for the html markup to
22897      * create a child element with the commensurate Default class name (<code>baseCls</code> will be
22898      * replaced by <code>{@link #baseCls}</code>):</p>
22899      * <pre>
22900      * Panel      Default  Default             Custom      Additional       Additional
22901      * Element    element  class               element     class            style
22902      * ========   ==========================   =========   ==============   ===========
22903      * {@link #header}     div      {@link #baseCls}+'-header'   {@link #headerCfg}   headerCssClass   headerStyle
22904      * {@link #bwrap}      div      {@link #baseCls}+'-bwrap'     {@link #bwrapCfg}    bwrapCssClass    bwrapStyle
22905      * + tbar     div      {@link #baseCls}+'-tbar'       {@link #tbarCfg}     tbarCssClass     tbarStyle
22906      * + {@link #body}     div      {@link #baseCls}+'-body'       {@link #bodyCfg}     {@link #bodyCssClass}     {@link #bodyStyle}
22907      * + bbar     div      {@link #baseCls}+'-bbar'       {@link #bbarCfg}     bbarCssClass     bbarStyle
22908      * + {@link #footer}   div      {@link #baseCls}+'-footer'   {@link #footerCfg}   footerCssClass   footerStyle
22909      * </pre>
22910      * <p>Configuring a Custom element may be used, for example, to force the {@link #body} Element
22911      * to use a different form of markup than is created by default. An example of this might be
22912      * to {@link Ext.Element#createChild create a child} Panel containing a custom content, such as
22913      * a header, or forcing centering of all Panel content by having the body be a &lt;center&gt;
22914      * element:</p>
22915      * <pre><code>
22916 new Ext.Panel({
22917     title: 'Message Title',
22918     renderTo: Ext.getBody(),
22919     width: 200, height: 130,
22920     <b>bodyCfg</b>: {
22921         tag: 'center',
22922         cls: 'x-panel-body',  // Default class not applied if Custom element specified
22923         html: 'Message'
22924     },
22925     footerCfg: {
22926         tag: 'h2',
22927         cls: 'x-panel-footer'        // same as the Default class
22928         html: 'footer html'
22929     },
22930     footerCssClass: 'custom-footer', // additional css class, see {@link Ext.element#addClass addClass}
22931     footerStyle:    'background-color:red' // see {@link #bodyStyle}
22932 });
22933      * </code></pre>
22934      * <p>The example above also explicitly creates a <code>{@link #footer}</code> with custom markup and
22935      * styling applied.</p>
22936      */
22937     /**
22938      * @cfg {Object} headerCfg
22939      * <p>A {@link Ext.DomHelper DomHelper} element specification object specifying the element structure
22940      * of this Panel's {@link #header} Element.  See <code>{@link #bodyCfg}</code> also.</p>
22941      */
22942     /**
22943      * @cfg {Object} bwrapCfg
22944      * <p>A {@link Ext.DomHelper DomHelper} element specification object specifying the element structure
22945      * of this Panel's {@link #bwrap} Element.  See <code>{@link #bodyCfg}</code> also.</p>
22946      */
22947     /**
22948      * @cfg {Object} tbarCfg
22949      * <p>A {@link Ext.DomHelper DomHelper} element specification object specifying the element structure
22950      * of this Panel's {@link #tbar} Element.  See <code>{@link #bodyCfg}</code> also.</p>
22951      */
22952     /**
22953      * @cfg {Object} bbarCfg
22954      * <p>A {@link Ext.DomHelper DomHelper} element specification object specifying the element structure
22955      * of this Panel's {@link #bbar} Element.  See <code>{@link #bodyCfg}</code> also.</p>
22956      */
22957     /**
22958      * @cfg {Object} footerCfg
22959      * <p>A {@link Ext.DomHelper DomHelper} element specification object specifying the element structure
22960      * of this Panel's {@link #footer} Element.  See <code>{@link #bodyCfg}</code> also.</p>
22961      */
22962     /**
22963      * @cfg {Boolean} closable
22964      * Panels themselves do not directly support being closed, but some Panel subclasses do (like
22965      * {@link Ext.Window}) or a Panel Class within an {@link Ext.TabPanel}.  Specify <code>true</code>
22966      * to enable closing in such situations. Defaults to <code>false</code>.
22967      */
22968     /**
22969      * The Panel's footer {@link Ext.Element Element}. Read-only.
22970      * <p>This Element is used to house the Panel's <code>{@link #buttons}</code> or <code>{@link #fbar}</code>.</p>
22971      * <br><p><b>Note</b>: see the Note for <code>{@link Ext.Component#el el}</code> also.</p>
22972      * @type Ext.Element
22973      * @property footer
22974      */
22975     /**
22976      * @cfg {Mixed} applyTo
22977      * <p>The id of the node, a DOM node or an existing Element corresponding to a DIV that is already present in
22978      * the document that specifies some panel-specific structural markup.  When <code>applyTo</code> is used,
22979      * constituent parts of the panel can be specified by CSS class name within the main element, and the panel
22980      * will automatically create those components from that markup. Any required components not specified in the
22981      * markup will be autogenerated if necessary.</p>
22982      * <p>The following class names are supported (baseCls will be replaced by {@link #baseCls}):</p>
22983      * <ul><li>baseCls + '-header'</li>
22984      * <li>baseCls + '-header-text'</li>
22985      * <li>baseCls + '-bwrap'</li>
22986      * <li>baseCls + '-tbar'</li>
22987      * <li>baseCls + '-body'</li>
22988      * <li>baseCls + '-bbar'</li>
22989      * <li>baseCls + '-footer'</li></ul>
22990      * <p>Using this config, a call to render() is not required.  If applyTo is specified, any value passed for
22991      * {@link #renderTo} will be ignored and the target element's parent node will automatically be used as the
22992      * panel's container.</p>
22993      */
22994     /**
22995      * @cfg {Object/Array} tbar
22996      * <p>The top toolbar of the panel. This can be a {@link Ext.Toolbar} object, a toolbar config, or an array of
22997      * buttons/button configs to be added to the toolbar.  Note that this is not available as a property after render.
22998      * To access the top toolbar after render, use {@link #getTopToolbar}.</p>
22999      * <p><b>Note:</b> Although a Toolbar may contain Field components, these will <b>not</b> be updated by a load
23000      * of an ancestor FormPanel. A Panel's toolbars are not part of the standard Container->Component hierarchy, and
23001      * so are not scanned to collect form items. However, the values <b>will</b> be submitted because form
23002      * submission parameters are collected from the DOM tree.</p>
23003      */
23004     /**
23005      * @cfg {Object/Array} bbar
23006      * <p>The bottom toolbar of the panel. This can be a {@link Ext.Toolbar} object, a toolbar config, or an array of
23007      * buttons/button configs to be added to the toolbar.  Note that this is not available as a property after render.
23008      * To access the bottom toolbar after render, use {@link #getBottomToolbar}.</p>
23009      * <p><b>Note:</b> Although a Toolbar may contain Field components, these will <b>not</b> be updated by a load
23010      * of an ancestor FormPanel. A Panel's toolbars are not part of the standard Container->Component hierarchy, and
23011      * so are not scanned to collect form items. However, the values <b>will</b> be submitted because form
23012      * submission parameters are collected from the DOM tree.</p>
23013      */
23014     /** @cfg {Object/Array} fbar
23015      * <p>A {@link Ext.Toolbar Toolbar} object, a Toolbar config, or an array of
23016      * {@link Ext.Button Button}s/{@link Ext.Button Button} configs, describing a {@link Ext.Toolbar Toolbar} to be rendered into this Panel's footer element.</p>
23017      * <p>After render, the <code>fbar</code> property will be an {@link Ext.Toolbar Toolbar} instance.</p>
23018      * <p>If <code>{@link #buttons}</code> are specified, they will supersede the <code>fbar</code> configuration property.</p>
23019      * The Panel's <code>{@link #buttonAlign}</code> configuration affects the layout of these items, for example:
23020      * <pre><code>
23021 var w = new Ext.Window({
23022     height: 250,
23023     width: 500,
23024     bbar: new Ext.Toolbar({
23025         items: [{
23026             text: 'bbar Left'
23027         },'->',{
23028             text: 'bbar Right'
23029         }]
23030     }),
23031     {@link #buttonAlign}: 'left', // anything but 'center' or 'right' and you can use '-', and '->'
23032                                   // to control the alignment of fbar items
23033     fbar: [{
23034         text: 'fbar Left'
23035     },'->',{
23036         text: 'fbar Right'
23037     }]
23038 }).show();
23039      * </code></pre>
23040      * <p><b>Note:</b> Although a Toolbar may contain Field components, these will <b>not</b> be updated by a load
23041      * of an ancestor FormPanel. A Panel's toolbars are not part of the standard Container->Component hierarchy, and
23042      * so are not scanned to collect form items. However, the values <b>will</b> be submitted because form
23043      * submission parameters are collected from the DOM tree.</p>
23044      */
23045     /**
23046      * @cfg {Boolean} header
23047      * <code>true</code> to create the Panel's header element explicitly, <code>false</code> to skip creating
23048      * it.  If a <code>{@link #title}</code> is set the header will be created automatically, otherwise it will not.
23049      * If a <code>{@link #title}</code> is set but <code>header</code> is explicitly set to <code>false</code>, the header
23050      * will not be rendered.
23051      */
23052     /**
23053      * @cfg {Boolean} footer
23054      * <code>true</code> to create the footer element explicitly, false to skip creating it. The footer
23055      * will be created automatically if <code>{@link #buttons}</code> or a <code>{@link #fbar}</code> have
23056      * been configured.  See <code>{@link #bodyCfg}</code> for an example.
23057      */
23058     /**
23059      * @cfg {String} title
23060      * The title text to be used as innerHTML (html tags are accepted) to display in the panel
23061      * <code>{@link #header}</code> (defaults to ''). When a <code>title</code> is specified the
23062      * <code>{@link #header}</code> element will automatically be created and displayed unless
23063      * {@link #header} is explicitly set to <code>false</code>.  If you do not want to specify a
23064      * <code>title</code> at config time, but you may want one later, you must either specify a non-empty
23065      * <code>title</code> (a blank space ' ' will do) or <code>header:true</code> so that the container
23066      * element will get created.
23067      */
23068     /**
23069      * @cfg {Array} buttons
23070      * <code>buttons</code> will be used as <code>{@link Ext.Container#items items}</code> for the toolbar in
23071      * the footer (<code>{@link #fbar}</code>). Typically the value of this configuration property will be
23072      * an array of {@link Ext.Button}s or {@link Ext.Button} configuration objects.
23073      * If an item is configured with <code>minWidth</code> or the Panel is configured with <code>minButtonWidth</code>,
23074      * that width will be applied to the item.
23075      */
23076     /**
23077      * @cfg {Object/String/Function} autoLoad
23078      * A valid url spec according to the Updater {@link Ext.Updater#update} method.
23079      * If autoLoad is not null, the panel will attempt to load its contents
23080      * immediately upon render.<p>
23081      * The URL will become the default URL for this panel's {@link #body} element,
23082      * so it may be {@link Ext.Element#refresh refresh}ed at any time.</p>
23083      */
23084     /**
23085      * @cfg {Boolean} frame
23086      * <code>false</code> by default to render with plain 1px square borders. <code>true</code> to render with
23087      * 9 elements, complete with custom rounded corners (also see {@link Ext.Element#boxWrap}).
23088      * <p>The template generated for each condition is depicted below:</p><pre><code>
23089      *
23090 // frame = false
23091 &lt;div id="developer-specified-id-goes-here" class="x-panel">
23092
23093     &lt;div class="x-panel-header">&lt;span class="x-panel-header-text">Title: (frame:false)&lt;/span>&lt;/div>
23094
23095     &lt;div class="x-panel-bwrap">
23096         &lt;div class="x-panel-body">&lt;p>html value goes here&lt;/p>&lt;/div>
23097     &lt;/div>
23098 &lt;/div>
23099
23100 // frame = true (create 9 elements)
23101 &lt;div id="developer-specified-id-goes-here" class="x-panel">
23102     &lt;div class="x-panel-tl">&lt;div class="x-panel-tr">&lt;div class="x-panel-tc">
23103         &lt;div class="x-panel-header">&lt;span class="x-panel-header-text">Title: (frame:true)&lt;/span>&lt;/div>
23104     &lt;/div>&lt;/div>&lt;/div>
23105
23106     &lt;div class="x-panel-bwrap">
23107         &lt;div class="x-panel-ml">&lt;div class="x-panel-mr">&lt;div class="x-panel-mc">
23108             &lt;div class="x-panel-body">&lt;p>html value goes here&lt;/p>&lt;/div>
23109         &lt;/div>&lt;/div>&lt;/div>
23110
23111         &lt;div class="x-panel-bl">&lt;div class="x-panel-br">&lt;div class="x-panel-bc"/>
23112         &lt;/div>&lt;/div>&lt;/div>
23113 &lt;/div>
23114      * </code></pre>
23115      */
23116     /**
23117      * @cfg {Boolean} border
23118      * True to display the borders of the panel's body element, false to hide them (defaults to true).  By default,
23119      * the border is a 2px wide inset border, but this can be further altered by setting {@link #bodyBorder} to false.
23120      */
23121     /**
23122      * @cfg {Boolean} bodyBorder
23123      * True to display an interior border on the body element of the panel, false to hide it (defaults to true).
23124      * This only applies when {@link #border} == true.  If border == true and bodyBorder == false, the border will display
23125      * as a 1px wide inset border, giving the entire body element an inset appearance.
23126      */
23127     /**
23128      * @cfg {String/Object/Function} bodyCssClass
23129      * Additional css class selector to be applied to the {@link #body} element in the format expected by
23130      * {@link Ext.Element#addClass} (defaults to null). See {@link #bodyCfg}.
23131      */
23132     /**
23133      * @cfg {String/Object/Function} bodyStyle
23134      * Custom CSS styles to be applied to the {@link #body} element in the format expected by
23135      * {@link Ext.Element#applyStyles} (defaults to null). See {@link #bodyCfg}.
23136      */
23137     /**
23138      * @cfg {String} iconCls
23139      * The CSS class selector that specifies a background image to be used as the header icon (defaults to '').
23140      * <p>An example of specifying a custom icon class would be something like:
23141      * </p><pre><code>
23142 // specify the property in the config for the class:
23143      ...
23144      iconCls: 'my-icon'
23145
23146 // css class that specifies background image to be used as the icon image:
23147 .my-icon { background-image: url(../images/my-icon.gif) 0 6px no-repeat !important; }
23148 </code></pre>
23149      */
23150     /**
23151      * @cfg {Boolean} collapsible
23152      * True to make the panel collapsible and have the expand/collapse toggle button automatically rendered into
23153      * the header tool button area, false to keep the panel statically sized with no button (defaults to false).
23154      */
23155     /**
23156      * @cfg {Array} tools
23157      * An array of tool button configs to be added to the header tool area. When rendered, each tool is
23158      * stored as an {@link Ext.Element Element} referenced by a public property called <code><b></b>tools.<i>&lt;tool-type&gt;</i></code>
23159      * <p>Each tool config may contain the following properties:
23160      * <div class="mdetail-params"><ul>
23161      * <li><b>id</b> : String<div class="sub-desc"><b>Required.</b> The type
23162      * of tool to create. By default, this assigns a CSS class of the form <code>x-tool-<i>&lt;tool-type&gt;</i></code> to the
23163      * resulting tool Element. Ext provides CSS rules, and an icon sprite containing images for the tool types listed below.
23164      * The developer may implement custom tools by supplying alternate CSS rules and background images:
23165      * <ul>
23166      * <div class="x-tool x-tool-toggle" style="float:left; margin-right:5;"> </div><div><code> toggle</code> (Created by default when {@link #collapsible} is <code>true</code>)</div>
23167      * <div class="x-tool x-tool-close" style="float:left; margin-right:5;"> </div><div><code> close</code></div>
23168      * <div class="x-tool x-tool-minimize" style="float:left; margin-right:5;"> </div><div><code> minimize</code></div>
23169      * <div class="x-tool x-tool-maximize" style="float:left; margin-right:5;"> </div><div><code> maximize</code></div>
23170      * <div class="x-tool x-tool-restore" style="float:left; margin-right:5;"> </div><div><code> restore</code></div>
23171      * <div class="x-tool x-tool-gear" style="float:left; margin-right:5;"> </div><div><code> gear</code></div>
23172      * <div class="x-tool x-tool-pin" style="float:left; margin-right:5;"> </div><div><code> pin</code></div>
23173      * <div class="x-tool x-tool-unpin" style="float:left; margin-right:5;"> </div><div><code> unpin</code></div>
23174      * <div class="x-tool x-tool-right" style="float:left; margin-right:5;"> </div><div><code> right</code></div>
23175      * <div class="x-tool x-tool-left" style="float:left; margin-right:5;"> </div><div><code> left</code></div>
23176      * <div class="x-tool x-tool-up" style="float:left; margin-right:5;"> </div><div><code> up</code></div>
23177      * <div class="x-tool x-tool-down" style="float:left; margin-right:5;"> </div><div><code> down</code></div>
23178      * <div class="x-tool x-tool-refresh" style="float:left; margin-right:5;"> </div><div><code> refresh</code></div>
23179      * <div class="x-tool x-tool-minus" style="float:left; margin-right:5;"> </div><div><code> minus</code></div>
23180      * <div class="x-tool x-tool-plus" style="float:left; margin-right:5;"> </div><div><code> plus</code></div>
23181      * <div class="x-tool x-tool-help" style="float:left; margin-right:5;"> </div><div><code> help</code></div>
23182      * <div class="x-tool x-tool-search" style="float:left; margin-right:5;"> </div><div><code> search</code></div>
23183      * <div class="x-tool x-tool-save" style="float:left; margin-right:5;"> </div><div><code> save</code></div>
23184      * <div class="x-tool x-tool-print" style="float:left; margin-right:5;"> </div><div><code> print</code></div>
23185      * </ul></div></li>
23186      * <li><b>handler</b> : Function<div class="sub-desc"><b>Required.</b> The function to
23187      * call when clicked. Arguments passed are:<ul>
23188      * <li><b>event</b> : Ext.EventObject<div class="sub-desc">The click event.</div></li>
23189      * <li><b>toolEl</b> : Ext.Element<div class="sub-desc">The tool Element.</div></li>
23190      * <li><b>panel</b> : Ext.Panel<div class="sub-desc">The host Panel</div></li>
23191      * <li><b>tc</b> : Object<div class="sub-desc">The tool configuration object</div></li>
23192      * </ul></div></li>
23193      * <li><b>stopEvent</b> : Boolean<div class="sub-desc">Defaults to true. Specify as false to allow click event to propagate.</div></li>
23194      * <li><b>scope</b> : Object<div class="sub-desc">The scope in which to call the handler.</div></li>
23195      * <li><b>qtip</b> : String/Object<div class="sub-desc">A tip string, or
23196      * a config argument to {@link Ext.QuickTip#register}</div></li>
23197      * <li><b>hidden</b> : Boolean<div class="sub-desc">True to initially render hidden.</div></li>
23198      * <li><b>on</b> : Object<div class="sub-desc">A listener config object specifiying
23199      * event listeners in the format of an argument to {@link #addListener}</div></li>
23200      * </ul></div>
23201      * <p>Note that, apart from the toggle tool which is provided when a panel is collapsible, these
23202      * tools only provide the visual button. Any required functionality must be provided by adding
23203      * handlers that implement the necessary behavior.</p>
23204      * <p>Example usage:</p>
23205      * <pre><code>
23206 tools:[{
23207     id:'refresh',
23208     qtip: 'Refresh form Data',
23209     // hidden:true,
23210     handler: function(event, toolEl, panel){
23211         // refresh logic
23212     }
23213 },
23214 {
23215     id:'help',
23216     qtip: 'Get Help',
23217     handler: function(event, toolEl, panel){
23218         // whatever
23219     }
23220 }]
23221 </code></pre>
23222      * <p>For the custom id of <code>'help'</code> define two relevant css classes with a link to
23223      * a 15x15 image:</p>
23224      * <pre><code>
23225 .x-tool-help {background-image: url(images/help.png);}
23226 .x-tool-help-over {background-image: url(images/help_over.png);}
23227 // if using an image sprite:
23228 .x-tool-help {background-image: url(images/help.png) no-repeat 0 0;}
23229 .x-tool-help-over {background-position:-15px 0;}
23230 </code></pre>
23231      */
23232     /**
23233      * @cfg {Ext.Template/Ext.XTemplate} toolTemplate
23234      * <p>A Template used to create {@link #tools} in the {@link #header} Element. Defaults to:</p><pre><code>
23235 new Ext.Template('&lt;div class="x-tool x-tool-{id}">&amp;#160;&lt;/div>')</code></pre>
23236      * <p>This may may be overridden to provide a custom DOM structure for tools based upon a more
23237      * complex XTemplate. The template's data is a single tool configuration object (Not the entire Array)
23238      * as specified in {@link #tools}.  In the following example an &lt;a> tag is used to provide a
23239      * visual indication when hovering over the tool:</p><pre><code>
23240 var win = new Ext.Window({
23241     tools: [{
23242         id: 'download',
23243         href: '/MyPdfDoc.pdf'
23244     }],
23245     toolTemplate: new Ext.XTemplate(
23246         '&lt;tpl if="id==\'download\'">',
23247             '&lt;a class="x-tool x-tool-pdf" href="{href}">&lt;/a>',
23248         '&lt;/tpl>',
23249         '&lt;tpl if="id!=\'download\'">',
23250             '&lt;div class="x-tool x-tool-{id}">&amp;#160;&lt;/div>',
23251         '&lt;/tpl>'
23252     ),
23253     width:500,
23254     height:300,
23255     closeAction:'hide'
23256 });</code></pre>
23257      * <p>Note that the CSS class 'x-tool-pdf' should have an associated style rule which provides an
23258      * appropriate background image, something like:</p>
23259     <pre><code>
23260     a.x-tool-pdf {background-image: url(../shared/extjs/images/pdf.gif)!important;}
23261     </code></pre>
23262      */
23263     /**
23264      * @cfg {Boolean} hideCollapseTool
23265      * <code>true</code> to hide the expand/collapse toggle button when <code>{@link #collapsible} == true</code>,
23266      * <code>false</code> to display it (defaults to <code>false</code>).
23267      */
23268     /**
23269      * @cfg {Boolean} titleCollapse
23270      * <code>true</code> to allow expanding and collapsing the panel (when <code>{@link #collapsible} = true</code>)
23271      * by clicking anywhere in the header bar, <code>false</code>) to allow it only by clicking to tool button
23272      * (defaults to <code>false</code>)). If this panel is a child item of a border layout also see the
23273      * {@link Ext.layout.BorderLayout.Region BorderLayout.Region}
23274      * <code>{@link Ext.layout.BorderLayout.Region#floatable floatable}</code> config option.
23275      */
23276
23277     /**
23278      * @cfg {Mixed} floating
23279      * <p>This property is used to configure the underlying {@link Ext.Layer}. Acceptable values for this
23280      * configuration property are:</p><div class="mdetail-params"><ul>
23281      * <li><b><code>false</code></b> : <b>Default.</b><div class="sub-desc">Display the panel inline where it is
23282      * rendered.</div></li>
23283      * <li><b><code>true</code></b> : <div class="sub-desc">Float the panel (absolute position it with automatic
23284      * shimming and shadow).<ul>
23285      * <div class="sub-desc">Setting floating to true will create an Ext.Layer for this panel and display the
23286      * panel at negative offsets so that it is hidden.</div>
23287      * <div class="sub-desc">Since the panel will be absolute positioned, the position must be set explicitly
23288      * <i>after</i> render (e.g., <code>myPanel.setPosition(100,100);</code>).</div>
23289      * <div class="sub-desc"><b>Note</b>: when floating a panel you should always assign a fixed width,
23290      * otherwise it will be auto width and will expand to fill to the right edge of the viewport.</div>
23291      * </ul></div></li>
23292      * <li><b><code>{@link Ext.Layer object}</code></b> : <div class="sub-desc">The specified object will be used
23293      * as the configuration object for the {@link Ext.Layer} that will be created.</div></li>
23294      * </ul></div>
23295      */
23296     /**
23297      * @cfg {Boolean/String} shadow
23298      * <code>true</code> (or a valid Ext.Shadow {@link Ext.Shadow#mode} value) to display a shadow behind the
23299      * panel, <code>false</code> to display no shadow (defaults to <code>'sides'</code>).  Note that this option
23300      * only applies when <code>{@link #floating} = true</code>.
23301      */
23302     /**
23303      * @cfg {Number} shadowOffset
23304      * The number of pixels to offset the shadow if displayed (defaults to <code>4</code>). Note that this
23305      * option only applies when <code>{@link #floating} = true</code>.
23306      */
23307     /**
23308      * @cfg {Boolean} shim
23309      * <code>false</code> to disable the iframe shim in browsers which need one (defaults to <code>true</code>).
23310      * Note that this option only applies when <code>{@link #floating} = true</code>.
23311      */
23312     /**
23313      * @cfg {Object/Array} keys
23314      * A {@link Ext.KeyMap} config object (in the format expected by {@link Ext.KeyMap#addBinding}
23315      * used to assign custom key handling to this panel (defaults to <code>null</code>).
23316      */
23317     /**
23318      * @cfg {Boolean/Object} draggable
23319      * <p><code>true</code> to enable dragging of this Panel (defaults to <code>false</code>).</p>
23320      * <p>For custom drag/drop implementations, an <b>Ext.Panel.DD</b> config could also be passed
23321      * in this config instead of <code>true</code>. Ext.Panel.DD is an internal, undocumented class which
23322      * moves a proxy Element around in place of the Panel's element, but provides no other behaviour
23323      * during dragging or on drop. It is a subclass of {@link Ext.dd.DragSource}, so behaviour may be
23324      * added by implementing the interface methods of {@link Ext.dd.DragDrop} e.g.:
23325      * <pre><code>
23326 new Ext.Panel({
23327     title: 'Drag me',
23328     x: 100,
23329     y: 100,
23330     renderTo: Ext.getBody(),
23331     floating: true,
23332     frame: true,
23333     width: 400,
23334     height: 200,
23335     draggable: {
23336 //      Config option of Ext.Panel.DD class.
23337 //      It&#39;s a floating Panel, so do not show a placeholder proxy in the original position.
23338         insertProxy: false,
23339
23340 //      Called for each mousemove event while dragging the DD object.
23341         onDrag : function(e){
23342 //          Record the x,y position of the drag proxy so that we can
23343 //          position the Panel at end of drag.
23344             var pel = this.proxy.getEl();
23345             this.x = pel.getLeft(true);
23346             this.y = pel.getTop(true);
23347
23348 //          Keep the Shadow aligned if there is one.
23349             var s = this.panel.getEl().shadow;
23350             if (s) {
23351                 s.realign(this.x, this.y, pel.getWidth(), pel.getHeight());
23352             }
23353         },
23354
23355 //      Called on the mouseup event.
23356         endDrag : function(e){
23357             this.panel.setPosition(this.x, this.y);
23358         }
23359     }
23360 }).show();
23361 </code></pre>
23362      */
23363     /**
23364      * @cfg {Boolean} disabled
23365      * Render this panel disabled (default is <code>false</code>). An important note when using the disabled
23366      * config on panels is that IE will often fail to initialize the disabled mask element correectly if
23367      * the panel's layout has not yet completed by the time the Panel is disabled during the render process.
23368      * If you experience this issue, you may need to instead use the {@link #afterlayout} event to initialize
23369      * the disabled state:
23370      * <pre><code>
23371 new Ext.Panel({
23372     ...
23373     listeners: {
23374         'afterlayout': {
23375             fn: function(p){
23376                 p.disable();
23377             },
23378             single: true // important, as many layouts can occur
23379         }
23380     }
23381 });
23382 </code></pre>
23383      */
23384     /**
23385      * @cfg {Boolean} autoHeight
23386      * <code>true</code> to use height:'auto', <code>false</code> to use fixed height (defaults to <code>false</code>).
23387      * <b>Note</b>: Setting <code>autoHeight: true</code> means that the browser will manage the panel's height
23388      * based on its contents, and that Ext will not manage it at all. If the panel is within a layout that
23389      * manages dimensions (<code>fit</code>, <code>border</code>, etc.) then setting <code>autoHeight: true</code>
23390      * can cause issues with scrolling and will not generally work as expected since the panel will take
23391      * on the height of its contents rather than the height required by the Ext layout.
23392      */
23393
23394
23395     /**
23396      * @cfg {String} baseCls
23397      * The base CSS class to apply to this panel's element (defaults to <code>'x-panel'</code>).
23398      * <p>Another option available by default is to specify <code>'x-plain'</code> which strips all styling
23399      * except for required attributes for Ext layouts to function (e.g. overflow:hidden).
23400      * See <code>{@link #unstyled}</code> also.</p>
23401      */
23402     baseCls : 'x-panel',
23403     /**
23404      * @cfg {String} collapsedCls
23405      * A CSS class to add to the panel's element after it has been collapsed (defaults to
23406      * <code>'x-panel-collapsed'</code>).
23407      */
23408     collapsedCls : 'x-panel-collapsed',
23409     /**
23410      * @cfg {Boolean} maskDisabled
23411      * <code>true</code> to mask the panel when it is {@link #disabled}, <code>false</code> to not mask it (defaults
23412      * to <code>true</code>).  Either way, the panel will always tell its contained elements to disable themselves
23413      * when it is disabled, but masking the panel can provide an additional visual cue that the panel is
23414      * disabled.
23415      */
23416     maskDisabled : true,
23417     /**
23418      * @cfg {Boolean} animCollapse
23419      * <code>true</code> to animate the transition when the panel is collapsed, <code>false</code> to skip the
23420      * animation (defaults to <code>true</code> if the {@link Ext.Fx} class is available, otherwise <code>false</code>).
23421      */
23422     animCollapse : Ext.enableFx,
23423     /**
23424      * @cfg {Boolean} headerAsText
23425      * <code>true</code> to display the panel <code>{@link #title}</code> in the <code>{@link #header}</code>,
23426      * <code>false</code> to hide it (defaults to <code>true</code>).
23427      */
23428     headerAsText : true,
23429     /**
23430      * @cfg {String} buttonAlign
23431      * The alignment of any {@link #buttons} added to this panel.  Valid values are <code>'right'</code>,
23432      * <code>'left'</code> and <code>'center'</code> (defaults to <code>'right'</code>).
23433      */
23434     buttonAlign : 'right',
23435     /**
23436      * @cfg {Boolean} collapsed
23437      * <code>true</code> to render the panel collapsed, <code>false</code> to render it expanded (defaults to
23438      * <code>false</code>).
23439      */
23440     collapsed : false,
23441     /**
23442      * @cfg {Boolean} collapseFirst
23443      * <code>true</code> to make sure the collapse/expand toggle button always renders first (to the left of)
23444      * any other tools in the panel's title bar, <code>false</code> to render it last (defaults to <code>true</code>).
23445      */
23446     collapseFirst : true,
23447     /**
23448      * @cfg {Number} minButtonWidth
23449      * Minimum width in pixels of all {@link #buttons} in this panel (defaults to <code>75</code>)
23450      */
23451     minButtonWidth : 75,
23452     /**
23453      * @cfg {Boolean} unstyled
23454      * Overrides the <code>{@link #baseCls}</code> setting to <code>{@link #baseCls} = 'x-plain'</code> which renders
23455      * the panel unstyled except for required attributes for Ext layouts to function (e.g. overflow:hidden).
23456      */
23457     /**
23458      * @cfg {String} elements
23459      * A comma-delimited list of panel elements to initialize when the panel is rendered.  Normally, this list will be
23460      * generated automatically based on the items added to the panel at config time, but sometimes it might be useful to
23461      * make sure a structural element is rendered even if not specified at config time (for example, you may want
23462      * to add a button or toolbar dynamically after the panel has been rendered).  Adding those elements to this
23463      * list will allocate the required placeholders in the panel when it is rendered.  Valid values are<div class="mdetail-params"><ul>
23464      * <li><code>header</code></li>
23465      * <li><code>tbar</code> (top bar)</li>
23466      * <li><code>body</code></li>
23467      * <li><code>bbar</code> (bottom bar)</li>
23468      * <li><code>footer</code></li>
23469      * </ul></div>
23470      * Defaults to '<code>body</code>'.
23471      */
23472     elements : 'body',
23473     /**
23474      * @cfg {Boolean} preventBodyReset
23475      * Defaults to <code>false</code>.  When set to <code>true</code>, an extra css class <code>'x-panel-normal'</code>
23476      * will be added to the panel's element, effectively applying css styles suggested by the W3C
23477      * (see http://www.w3.org/TR/CSS21/sample.html) to the Panel's <b>body</b> element (not the header,
23478      * footer, etc.).
23479      */
23480     preventBodyReset : false,
23481
23482     /**
23483      * @cfg {Number/String} padding
23484      * A shortcut for setting a padding style on the body element. The value can either be
23485      * a number to be applied to all sides, or a normal css string describing padding.
23486      * Defaults to <tt>undefined</tt>.
23487      *
23488      */
23489     padding: undefined,
23490
23491     /** @cfg {String} resizeEvent
23492      * The event to listen to for resizing in layouts. Defaults to <tt>'bodyresize'</tt>.
23493      */
23494     resizeEvent: 'bodyresize',
23495
23496     // protected - these could be used to customize the behavior of the window,
23497     // but changing them would not be useful without further mofifications and
23498     // could lead to unexpected or undesirable results.
23499     toolTarget : 'header',
23500     collapseEl : 'bwrap',
23501     slideAnchor : 't',
23502     disabledClass : '',
23503
23504     // private, notify box this class will handle heights
23505     deferHeight : true,
23506     // private
23507     expandDefaults: {
23508         duration : 0.25
23509     },
23510     // private
23511     collapseDefaults : {
23512         duration : 0.25
23513     },
23514
23515     // private
23516     initComponent : function(){
23517         Ext.Panel.superclass.initComponent.call(this);
23518
23519         this.addEvents(
23520             /**
23521              * @event bodyresize
23522              * Fires after the Panel has been resized.
23523              * @param {Ext.Panel} p the Panel which has been resized.
23524              * @param {Number} width The Panel body's new width.
23525              * @param {Number} height The Panel body's new height.
23526              */
23527             'bodyresize',
23528             /**
23529              * @event titlechange
23530              * Fires after the Panel title has been {@link #title set} or {@link #setTitle changed}.
23531              * @param {Ext.Panel} p the Panel which has had its title changed.
23532              * @param {String} The new title.
23533              */
23534             'titlechange',
23535             /**
23536              * @event iconchange
23537              * Fires after the Panel icon class has been {@link #iconCls set} or {@link #setIconClass changed}.
23538              * @param {Ext.Panel} p the Panel which has had its {@link #iconCls icon class} changed.
23539              * @param {String} The new icon class.
23540              * @param {String} The old icon class.
23541              */
23542             'iconchange',
23543             /**
23544              * @event collapse
23545              * Fires after the Panel has been collapsed.
23546              * @param {Ext.Panel} p the Panel that has been collapsed.
23547              */
23548             'collapse',
23549             /**
23550              * @event expand
23551              * Fires after the Panel has been expanded.
23552              * @param {Ext.Panel} p The Panel that has been expanded.
23553              */
23554             'expand',
23555             /**
23556              * @event beforecollapse
23557              * Fires before the Panel is collapsed.  A handler can return false to cancel the collapse.
23558              * @param {Ext.Panel} p the Panel being collapsed.
23559              * @param {Boolean} animate True if the collapse is animated, else false.
23560              */
23561             'beforecollapse',
23562             /**
23563              * @event beforeexpand
23564              * Fires before the Panel is expanded.  A handler can return false to cancel the expand.
23565              * @param {Ext.Panel} p The Panel being expanded.
23566              * @param {Boolean} animate True if the expand is animated, else false.
23567              */
23568             'beforeexpand',
23569             /**
23570              * @event beforeclose
23571              * Fires before the Panel is closed.  Note that Panels do not directly support being closed, but some
23572              * Panel subclasses do (like {@link Ext.Window}) or a Panel within a Ext.TabPanel.  This event only
23573              * applies to such subclasses.
23574              * A handler can return false to cancel the close.
23575              * @param {Ext.Panel} p The Panel being closed.
23576              */
23577             'beforeclose',
23578             /**
23579              * @event close
23580              * Fires after the Panel is closed.  Note that Panels do not directly support being closed, but some
23581              * Panel subclasses do (like {@link Ext.Window}) or a Panel within a Ext.TabPanel.
23582              * @param {Ext.Panel} p The Panel that has been closed.
23583              */
23584             'close',
23585             /**
23586              * @event activate
23587              * Fires after the Panel has been visually activated.
23588              * Note that Panels do not directly support being activated, but some Panel subclasses
23589              * do (like {@link Ext.Window}). Panels which are child Components of a TabPanel fire the
23590              * activate and deactivate events under the control of the TabPanel.
23591              * @param {Ext.Panel} p The Panel that has been activated.
23592              */
23593             'activate',
23594             /**
23595              * @event deactivate
23596              * Fires after the Panel has been visually deactivated.
23597              * Note that Panels do not directly support being deactivated, but some Panel subclasses
23598              * do (like {@link Ext.Window}). Panels which are child Components of a TabPanel fire the
23599              * activate and deactivate events under the control of the TabPanel.
23600              * @param {Ext.Panel} p The Panel that has been deactivated.
23601              */
23602             'deactivate'
23603         );
23604
23605         if(this.unstyled){
23606             this.baseCls = 'x-plain';
23607         }
23608
23609
23610         this.toolbars = [];
23611         // shortcuts
23612         if(this.tbar){
23613             this.elements += ',tbar';
23614             this.topToolbar = this.createToolbar(this.tbar);
23615             delete this.tbar;
23616
23617         }
23618         if(this.bbar){
23619             this.elements += ',bbar';
23620             this.bottomToolbar = this.createToolbar(this.bbar);
23621             delete this.bbar;
23622         }
23623
23624         if(this.header === true){
23625             this.elements += ',header';
23626             delete this.header;
23627         }else if(this.headerCfg || (this.title && this.header !== false)){
23628             this.elements += ',header';
23629         }
23630
23631         if(this.footerCfg || this.footer === true){
23632             this.elements += ',footer';
23633             delete this.footer;
23634         }
23635
23636         if(this.buttons){
23637             this.fbar = this.buttons;
23638             delete this.buttons;
23639         }
23640         if(this.fbar){
23641             this.createFbar(this.fbar);
23642         }
23643         if(this.autoLoad){
23644             this.on('render', this.doAutoLoad, this, {delay:10});
23645         }
23646     },
23647
23648     // private
23649     createFbar : function(fbar){
23650         var min = this.minButtonWidth;
23651         this.elements += ',footer';
23652         this.fbar = this.createToolbar(fbar, {
23653             buttonAlign: this.buttonAlign,
23654             toolbarCls: 'x-panel-fbar',
23655             enableOverflow: false,
23656             defaults: function(c){
23657                 return {
23658                     minWidth: c.minWidth || min
23659                 };
23660             }
23661         });
23662         //@compat addButton and buttons could possibly be removed
23663         //@target 4.0
23664         /**
23665          * This Panel's Array of buttons as created from the <code>{@link #buttons}</code>
23666          * config property. Read only.
23667          * @type Array
23668          * @property buttons
23669          */
23670         this.fbar.items.each(function(c){
23671             c.minWidth = c.minWidth || this.minButtonWidth;
23672         }, this);
23673         this.buttons = this.fbar.items.items;
23674     },
23675
23676     // private
23677     createToolbar: function(tb, options){
23678         var result;
23679         // Convert array to proper toolbar config
23680         if(Ext.isArray(tb)){
23681             tb = {
23682                 items: tb
23683             };
23684         }
23685         result = tb.events ? Ext.apply(tb, options) : this.createComponent(Ext.apply({}, tb, options), 'toolbar');
23686         this.toolbars.push(result);
23687         return result;
23688     },
23689
23690     // private
23691     createElement : function(name, pnode){
23692         if(this[name]){
23693             pnode.appendChild(this[name].dom);
23694             return;
23695         }
23696
23697         if(name === 'bwrap' || this.elements.indexOf(name) != -1){
23698             if(this[name+'Cfg']){
23699                 this[name] = Ext.fly(pnode).createChild(this[name+'Cfg']);
23700             }else{
23701                 var el = document.createElement('div');
23702                 el.className = this[name+'Cls'];
23703                 this[name] = Ext.get(pnode.appendChild(el));
23704             }
23705             if(this[name+'CssClass']){
23706                 this[name].addClass(this[name+'CssClass']);
23707             }
23708             if(this[name+'Style']){
23709                 this[name].applyStyles(this[name+'Style']);
23710             }
23711         }
23712     },
23713
23714     // private
23715     onRender : function(ct, position){
23716         Ext.Panel.superclass.onRender.call(this, ct, position);
23717         this.createClasses();
23718
23719         var el = this.el,
23720             d = el.dom,
23721             bw,
23722             ts;
23723
23724
23725         if(this.collapsible && !this.hideCollapseTool){
23726             this.tools = this.tools ? this.tools.slice(0) : [];
23727             this.tools[this.collapseFirst?'unshift':'push']({
23728                 id: 'toggle',
23729                 handler : this.toggleCollapse,
23730                 scope: this
23731             });
23732         }
23733
23734         if(this.tools){
23735             ts = this.tools;
23736             this.elements += (this.header !== false) ? ',header' : '';
23737         }
23738         this.tools = {};
23739
23740         el.addClass(this.baseCls);
23741         if(d.firstChild){ // existing markup
23742             this.header = el.down('.'+this.headerCls);
23743             this.bwrap = el.down('.'+this.bwrapCls);
23744             var cp = this.bwrap ? this.bwrap : el;
23745             this.tbar = cp.down('.'+this.tbarCls);
23746             this.body = cp.down('.'+this.bodyCls);
23747             this.bbar = cp.down('.'+this.bbarCls);
23748             this.footer = cp.down('.'+this.footerCls);
23749             this.fromMarkup = true;
23750         }
23751         if (this.preventBodyReset === true) {
23752             el.addClass('x-panel-reset');
23753         }
23754         if(this.cls){
23755             el.addClass(this.cls);
23756         }
23757
23758         if(this.buttons){
23759             this.elements += ',footer';
23760         }
23761
23762         // This block allows for maximum flexibility and performance when using existing markup
23763
23764         // framing requires special markup
23765         if(this.frame){
23766             el.insertHtml('afterBegin', String.format(Ext.Element.boxMarkup, this.baseCls));
23767
23768             this.createElement('header', d.firstChild.firstChild.firstChild);
23769             this.createElement('bwrap', d);
23770
23771             // append the mid and bottom frame to the bwrap
23772             bw = this.bwrap.dom;
23773             var ml = d.childNodes[1], bl = d.childNodes[2];
23774             bw.appendChild(ml);
23775             bw.appendChild(bl);
23776
23777             var mc = bw.firstChild.firstChild.firstChild;
23778             this.createElement('tbar', mc);
23779             this.createElement('body', mc);
23780             this.createElement('bbar', mc);
23781             this.createElement('footer', bw.lastChild.firstChild.firstChild);
23782
23783             if(!this.footer){
23784                 this.bwrap.dom.lastChild.className += ' x-panel-nofooter';
23785             }
23786             /*
23787              * Store a reference to this element so:
23788              * a) We aren't looking it up all the time
23789              * b) The last element is reported incorrectly when using a loadmask
23790              */
23791             this.ft = Ext.get(this.bwrap.dom.lastChild);
23792             this.mc = Ext.get(mc);
23793         }else{
23794             this.createElement('header', d);
23795             this.createElement('bwrap', d);
23796
23797             // append the mid and bottom frame to the bwrap
23798             bw = this.bwrap.dom;
23799             this.createElement('tbar', bw);
23800             this.createElement('body', bw);
23801             this.createElement('bbar', bw);
23802             this.createElement('footer', bw);
23803
23804             if(!this.header){
23805                 this.body.addClass(this.bodyCls + '-noheader');
23806                 if(this.tbar){
23807                     this.tbar.addClass(this.tbarCls + '-noheader');
23808                 }
23809             }
23810         }
23811
23812         if(Ext.isDefined(this.padding)){
23813             this.body.setStyle('padding', this.body.addUnits(this.padding));
23814         }
23815
23816         if(this.border === false){
23817             this.el.addClass(this.baseCls + '-noborder');
23818             this.body.addClass(this.bodyCls + '-noborder');
23819             if(this.header){
23820                 this.header.addClass(this.headerCls + '-noborder');
23821             }
23822             if(this.footer){
23823                 this.footer.addClass(this.footerCls + '-noborder');
23824             }
23825             if(this.tbar){
23826                 this.tbar.addClass(this.tbarCls + '-noborder');
23827             }
23828             if(this.bbar){
23829                 this.bbar.addClass(this.bbarCls + '-noborder');
23830             }
23831         }
23832
23833         if(this.bodyBorder === false){
23834            this.body.addClass(this.bodyCls + '-noborder');
23835         }
23836
23837         this.bwrap.enableDisplayMode('block');
23838
23839         if(this.header){
23840             this.header.unselectable();
23841
23842             // for tools, we need to wrap any existing header markup
23843             if(this.headerAsText){
23844                 this.header.dom.innerHTML =
23845                     '<span class="' + this.headerTextCls + '">'+this.header.dom.innerHTML+'</span>';
23846
23847                 if(this.iconCls){
23848                     this.setIconClass(this.iconCls);
23849                 }
23850             }
23851         }
23852
23853         if(this.floating){
23854             this.makeFloating(this.floating);
23855         }
23856
23857         if(this.collapsible && this.titleCollapse && this.header){
23858             this.mon(this.header, 'click', this.toggleCollapse, this);
23859             this.header.setStyle('cursor', 'pointer');
23860         }
23861         if(ts){
23862             this.addTool.apply(this, ts);
23863         }
23864
23865         // Render Toolbars.
23866         if(this.fbar){
23867             this.footer.addClass('x-panel-btns');
23868             this.fbar.ownerCt = this;
23869             this.fbar.render(this.footer);
23870             this.footer.createChild({cls:'x-clear'});
23871         }
23872         if(this.tbar && this.topToolbar){
23873             this.topToolbar.ownerCt = this;
23874             this.topToolbar.render(this.tbar);
23875         }
23876         if(this.bbar && this.bottomToolbar){
23877             this.bottomToolbar.ownerCt = this;
23878             this.bottomToolbar.render(this.bbar);
23879         }
23880     },
23881
23882     /**
23883      * Sets the CSS class that provides the icon image for this panel.  This method will replace any existing
23884      * icon class if one has already been set and fire the {@link #iconchange} event after completion.
23885      * @param {String} cls The new CSS class name
23886      */
23887     setIconClass : function(cls){
23888         var old = this.iconCls;
23889         this.iconCls = cls;
23890         if(this.rendered && this.header){
23891             if(this.frame){
23892                 this.header.addClass('x-panel-icon');
23893                 this.header.replaceClass(old, this.iconCls);
23894             }else{
23895                 var hd = this.header,
23896                     img = hd.child('img.x-panel-inline-icon');
23897                 if(img){
23898                     Ext.fly(img).replaceClass(old, this.iconCls);
23899                 }else{
23900                     Ext.DomHelper.insertBefore(hd.dom.firstChild, {
23901                         tag:'img', src: Ext.BLANK_IMAGE_URL, cls:'x-panel-inline-icon '+this.iconCls
23902                     });
23903                  }
23904             }
23905         }
23906         this.fireEvent('iconchange', this, cls, old);
23907     },
23908
23909     // private
23910     makeFloating : function(cfg){
23911         this.floating = true;
23912         this.el = new Ext.Layer(Ext.apply({}, cfg, {
23913             shadow: Ext.isDefined(this.shadow) ? this.shadow : 'sides',
23914             shadowOffset: this.shadowOffset,
23915             constrain:false,
23916             shim: this.shim === false ? false : undefined
23917         }), this.el);
23918     },
23919
23920     /**
23921      * Returns the {@link Ext.Toolbar toolbar} from the top (<code>{@link #tbar}</code>) section of the panel.
23922      * @return {Ext.Toolbar} The toolbar
23923      */
23924     getTopToolbar : function(){
23925         return this.topToolbar;
23926     },
23927
23928     /**
23929      * Returns the {@link Ext.Toolbar toolbar} from the bottom (<code>{@link #bbar}</code>) section of the panel.
23930      * @return {Ext.Toolbar} The toolbar
23931      */
23932     getBottomToolbar : function(){
23933         return this.bottomToolbar;
23934     },
23935
23936     /**
23937      * Adds a button to this panel.  Note that this method must be called prior to rendering.  The preferred
23938      * approach is to add buttons via the {@link #buttons} config.
23939      * @param {String/Object} config A valid {@link Ext.Button} config.  A string will become the text for a default
23940      * button config, an object will be treated as a button config object.
23941      * @param {Function} handler The function to be called on button {@link Ext.Button#click}
23942      * @param {Object} scope The scope (<code>this</code> reference) in which the button handler function is executed. Defaults to the Button.
23943      * @return {Ext.Button} The button that was added
23944      */
23945     addButton : function(config, handler, scope){
23946         if(!this.fbar){
23947             this.createFbar([]);
23948         }
23949         if(handler){
23950             if(Ext.isString(config)){
23951                 config = {text: config};
23952             }
23953             config = Ext.apply({
23954                 handler: handler,
23955                 scope: scope
23956             }, config)
23957         }
23958         return this.fbar.add(config);
23959     },
23960
23961     // private
23962     addTool : function(){
23963         if(!this.rendered){
23964             if(!this.tools){
23965                 this.tools = [];
23966             }
23967             Ext.each(arguments, function(arg){
23968                 this.tools.push(arg)
23969             }, this);
23970             return;
23971         }
23972          // nowhere to render tools!
23973         if(!this[this.toolTarget]){
23974             return;
23975         }
23976         if(!this.toolTemplate){
23977             // initialize the global tool template on first use
23978             var tt = new Ext.Template(
23979                  '<div class="x-tool x-tool-{id}">&#160;</div>'
23980             );
23981             tt.disableFormats = true;
23982             tt.compile();
23983             Ext.Panel.prototype.toolTemplate = tt;
23984         }
23985         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
23986             var tc = a[i];
23987             if(!this.tools[tc.id]){
23988                 var overCls = 'x-tool-'+tc.id+'-over';
23989                 var t = this.toolTemplate.insertFirst(this[this.toolTarget], tc, true);
23990                 this.tools[tc.id] = t;
23991                 t.enableDisplayMode('block');
23992                 this.mon(t, 'click',  this.createToolHandler(t, tc, overCls, this));
23993                 if(tc.on){
23994                     this.mon(t, tc.on);
23995                 }
23996                 if(tc.hidden){
23997                     t.hide();
23998                 }
23999                 if(tc.qtip){
24000                     if(Ext.isObject(tc.qtip)){
24001                         Ext.QuickTips.register(Ext.apply({
24002                               target: t.id
24003                         }, tc.qtip));
24004                     } else {
24005                         t.dom.qtip = tc.qtip;
24006                     }
24007                 }
24008                 t.addClassOnOver(overCls);
24009             }
24010         }
24011     },
24012
24013     onLayout : function(shallow, force){
24014         Ext.Panel.superclass.onLayout.apply(this, arguments);
24015         if(this.hasLayout && this.toolbars.length > 0){
24016             Ext.each(this.toolbars, function(tb){
24017                 tb.doLayout(undefined, force);
24018             });
24019             this.syncHeight();
24020         }
24021     },
24022
24023     syncHeight : function(){
24024         var h = this.toolbarHeight,
24025                 bd = this.body,
24026                 lsh = this.lastSize.height,
24027                 sz;
24028
24029         if(this.autoHeight || !Ext.isDefined(lsh) || lsh == 'auto'){
24030             return;
24031         }
24032
24033
24034         if(h != this.getToolbarHeight()){
24035             h = Math.max(0, this.adjustBodyHeight(lsh - this.getFrameHeight()));
24036             bd.setHeight(h);
24037             sz = bd.getSize();
24038             this.toolbarHeight = this.getToolbarHeight();
24039             this.onBodyResize(sz.width, sz.height);
24040         }
24041     },
24042
24043     // private
24044     onShow : function(){
24045         if(this.floating){
24046             return this.el.show();
24047         }
24048         Ext.Panel.superclass.onShow.call(this);
24049     },
24050
24051     // private
24052     onHide : function(){
24053         if(this.floating){
24054             return this.el.hide();
24055         }
24056         Ext.Panel.superclass.onHide.call(this);
24057     },
24058
24059     // private
24060     createToolHandler : function(t, tc, overCls, panel){
24061         return function(e){
24062             t.removeClass(overCls);
24063             if(tc.stopEvent !== false){
24064                 e.stopEvent();
24065             }
24066             if(tc.handler){
24067                 tc.handler.call(tc.scope || t, e, t, panel, tc);
24068             }
24069         };
24070     },
24071
24072     // private
24073     afterRender : function(){
24074         if(this.floating && !this.hidden){
24075             this.el.show();
24076         }
24077         if(this.title){
24078             this.setTitle(this.title);
24079         }
24080         Ext.Panel.superclass.afterRender.call(this); // do sizing calcs last
24081         if (this.collapsed) {
24082             this.collapsed = false;
24083             this.collapse(false);
24084         }
24085         this.initEvents();
24086     },
24087
24088     // private
24089     getKeyMap : function(){
24090         if(!this.keyMap){
24091             this.keyMap = new Ext.KeyMap(this.el, this.keys);
24092         }
24093         return this.keyMap;
24094     },
24095
24096     // private
24097     initEvents : function(){
24098         if(this.keys){
24099             this.getKeyMap();
24100         }
24101         if(this.draggable){
24102             this.initDraggable();
24103         }
24104         if(this.toolbars.length > 0){
24105             Ext.each(this.toolbars, function(tb){
24106                 tb.doLayout();
24107                 tb.on({
24108                     scope: this,
24109                     afterlayout: this.syncHeight,
24110                     remove: this.syncHeight
24111                 });
24112             }, this);
24113             this.syncHeight();
24114         }
24115
24116     },
24117
24118     // private
24119     initDraggable : function(){
24120         /**
24121          * <p>If this Panel is configured {@link #draggable}, this property will contain
24122          * an instance of {@link Ext.dd.DragSource} which handles dragging the Panel.</p>
24123          * The developer must provide implementations of the abstract methods of {@link Ext.dd.DragSource}
24124          * in order to supply behaviour for each stage of the drag/drop process. See {@link #draggable}.
24125          * @type Ext.dd.DragSource.
24126          * @property dd
24127          */
24128         this.dd = new Ext.Panel.DD(this, Ext.isBoolean(this.draggable) ? null : this.draggable);
24129     },
24130
24131     // private
24132     beforeEffect : function(anim){
24133         if(this.floating){
24134             this.el.beforeAction();
24135         }
24136         if(anim !== false){
24137             this.el.addClass('x-panel-animated');
24138         }
24139     },
24140
24141     // private
24142     afterEffect : function(anim){
24143         this.syncShadow();
24144         if(anim !== false){
24145             this.el.removeClass('x-panel-animated');
24146         }
24147     },
24148
24149     // private - wraps up an animation param with internal callbacks
24150     createEffect : function(a, cb, scope){
24151         var o = {
24152             scope:scope,
24153             block:true
24154         };
24155         if(a === true){
24156             o.callback = cb;
24157             return o;
24158         }else if(!a.callback){
24159             o.callback = cb;
24160         }else { // wrap it up
24161             o.callback = function(){
24162                 cb.call(scope);
24163                 Ext.callback(a.callback, a.scope);
24164             };
24165         }
24166         return Ext.applyIf(o, a);
24167     },
24168
24169     /**
24170      * Collapses the panel body so that it becomes hidden.  Fires the {@link #beforecollapse} event which will
24171      * cancel the collapse action if it returns false.
24172      * @param {Boolean} animate True to animate the transition, else false (defaults to the value of the
24173      * {@link #animCollapse} panel config)
24174      * @return {Ext.Panel} this
24175      */
24176     collapse : function(animate){
24177         if(this.collapsed || this.el.hasFxBlock() || this.fireEvent('beforecollapse', this, animate) === false){
24178             return;
24179         }
24180         var doAnim = animate === true || (animate !== false && this.animCollapse);
24181         this.beforeEffect(doAnim);
24182         this.onCollapse(doAnim, animate);
24183         return this;
24184     },
24185
24186     // private
24187     onCollapse : function(doAnim, animArg){
24188         if(doAnim){
24189             this[this.collapseEl].slideOut(this.slideAnchor,
24190                     Ext.apply(this.createEffect(animArg||true, this.afterCollapse, this),
24191                         this.collapseDefaults));
24192         }else{
24193             this[this.collapseEl].hide();
24194             this.afterCollapse(false);
24195         }
24196     },
24197
24198     // private
24199     afterCollapse : function(anim){
24200         this.collapsed = true;
24201         this.el.addClass(this.collapsedCls);
24202         this.afterEffect(anim);
24203         this.fireEvent('collapse', this);
24204     },
24205
24206     /**
24207      * Expands the panel body so that it becomes visible.  Fires the {@link #beforeexpand} event which will
24208      * cancel the expand action if it returns false.
24209      * @param {Boolean} animate True to animate the transition, else false (defaults to the value of the
24210      * {@link #animCollapse} panel config)
24211      * @return {Ext.Panel} this
24212      */
24213     expand : function(animate){
24214         if(!this.collapsed || this.el.hasFxBlock() || this.fireEvent('beforeexpand', this, animate) === false){
24215             return;
24216         }
24217         var doAnim = animate === true || (animate !== false && this.animCollapse);
24218         this.el.removeClass(this.collapsedCls);
24219         this.beforeEffect(doAnim);
24220         this.onExpand(doAnim, animate);
24221         return this;
24222     },
24223
24224     // private
24225     onExpand : function(doAnim, animArg){
24226         if(doAnim){
24227             this[this.collapseEl].slideIn(this.slideAnchor,
24228                     Ext.apply(this.createEffect(animArg||true, this.afterExpand, this),
24229                         this.expandDefaults));
24230         }else{
24231             this[this.collapseEl].show();
24232             this.afterExpand(false);
24233         }
24234     },
24235
24236     // private
24237     afterExpand : function(anim){
24238         this.collapsed = false;
24239         this.afterEffect(anim);
24240         if (this.deferLayout) {
24241             delete this.deferLayout;
24242             this.doLayout(true);
24243         }
24244         this.fireEvent('expand', this);
24245     },
24246
24247     /**
24248      * Shortcut for performing an {@link #expand} or {@link #collapse} based on the current state of the panel.
24249      * @param {Boolean} animate True to animate the transition, else false (defaults to the value of the
24250      * {@link #animCollapse} panel config)
24251      * @return {Ext.Panel} this
24252      */
24253     toggleCollapse : function(animate){
24254         this[this.collapsed ? 'expand' : 'collapse'](animate);
24255         return this;
24256     },
24257
24258     // private
24259     onDisable : function(){
24260         if(this.rendered && this.maskDisabled){
24261             this.el.mask();
24262         }
24263         Ext.Panel.superclass.onDisable.call(this);
24264     },
24265
24266     // private
24267     onEnable : function(){
24268         if(this.rendered && this.maskDisabled){
24269             this.el.unmask();
24270         }
24271         Ext.Panel.superclass.onEnable.call(this);
24272     },
24273
24274     // private
24275     onResize : function(w, h){
24276         if(Ext.isDefined(w) || Ext.isDefined(h)){
24277             if(!this.collapsed){
24278                 // First, set the the Panel's body width.
24279                 // If we have auto-widthed it, get the resulting full offset width so we can size the Toolbars to match
24280                 // The Toolbars must not buffer this resize operation because we need to know their heights.
24281
24282                 if(Ext.isNumber(w)){
24283                     this.body.setWidth(w = this.adjustBodyWidth(w - this.getFrameWidth()));
24284                 } else if (w == 'auto') {
24285                     w = this.body.setWidth('auto').dom.offsetWidth;
24286                 } else {
24287                     w = this.body.dom.offsetWidth;
24288                 }
24289
24290                 if(this.tbar){
24291                     this.tbar.setWidth(w);
24292                     if(this.topToolbar){
24293                         this.topToolbar.setSize(w);
24294                     }
24295                 }
24296                 if(this.bbar){
24297                     this.bbar.setWidth(w);
24298                     if(this.bottomToolbar){
24299                         this.bottomToolbar.setSize(w);
24300                         // The bbar does not move on resize without this.
24301                         if (Ext.isIE) {
24302                             this.bbar.setStyle('position', 'static');
24303                             this.bbar.setStyle('position', '');
24304                         }
24305                     }
24306                 }
24307                 if(this.footer){
24308                     this.footer.setWidth(w);
24309                     if(this.fbar){
24310                         this.fbar.setSize(Ext.isIE ? (w - this.footer.getFrameWidth('lr')) : 'auto');
24311                     }
24312                 }
24313
24314                 // At this point, the Toolbars must be layed out for getFrameHeight to find a result.
24315                 if(Ext.isNumber(h)){
24316                     h = Math.max(0, this.adjustBodyHeight(h - this.getFrameHeight()));
24317                     this.body.setHeight(h);
24318                 }else if(h == 'auto'){
24319                     this.body.setHeight(h);
24320                 }
24321
24322                 if(this.disabled && this.el._mask){
24323                     this.el._mask.setSize(this.el.dom.clientWidth, this.el.getHeight());
24324                 }
24325             }else{
24326                 // Adds an event to set the correct height afterExpand.  This accounts for the deferHeight flag in panel
24327                 this.queuedBodySize = {width: w, height: h};
24328                 if(!this.queuedExpand && this.allowQueuedExpand !== false){
24329                     this.queuedExpand = true;
24330                     this.on('expand', function(){
24331                         delete this.queuedExpand;
24332                         this.onResize(this.queuedBodySize.width, this.queuedBodySize.height);
24333                     }, this, {single:true});
24334                 }
24335             }
24336             this.onBodyResize(w, h);
24337         }
24338         this.syncShadow();
24339         Ext.Panel.superclass.onResize.call(this);
24340     },
24341
24342     // private
24343     onBodyResize: function(w, h){
24344         this.fireEvent('bodyresize', this, w, h);
24345     },
24346
24347     // private
24348     getToolbarHeight: function(){
24349         var h = 0;
24350         if(this.rendered){
24351             Ext.each(this.toolbars, function(tb){
24352                 h += tb.getHeight();
24353             }, this);
24354         }
24355         return h;
24356     },
24357
24358     // private
24359     adjustBodyHeight : function(h){
24360         return h;
24361     },
24362
24363     // private
24364     adjustBodyWidth : function(w){
24365         return w;
24366     },
24367
24368     // private
24369     onPosition : function(){
24370         this.syncShadow();
24371     },
24372
24373     /**
24374      * Returns the width in pixels of the framing elements of this panel (not including the body width).  To
24375      * retrieve the body width see {@link #getInnerWidth}.
24376      * @return {Number} The frame width
24377      */
24378     getFrameWidth : function(){
24379         var w = this.el.getFrameWidth('lr') + this.bwrap.getFrameWidth('lr');
24380
24381         if(this.frame){
24382             var l = this.bwrap.dom.firstChild;
24383             w += (Ext.fly(l).getFrameWidth('l') + Ext.fly(l.firstChild).getFrameWidth('r'));
24384             w += this.mc.getFrameWidth('lr');
24385         }
24386         return w;
24387     },
24388
24389     /**
24390      * Returns the height in pixels of the framing elements of this panel (including any top and bottom bars and
24391      * header and footer elements, but not including the body height).  To retrieve the body height see {@link #getInnerHeight}.
24392      * @return {Number} The frame height
24393      */
24394     getFrameHeight : function(){
24395         var h  = this.el.getFrameWidth('tb') + this.bwrap.getFrameWidth('tb');
24396         h += (this.tbar ? this.tbar.getHeight() : 0) +
24397              (this.bbar ? this.bbar.getHeight() : 0);
24398
24399         if(this.frame){
24400             h += this.el.dom.firstChild.offsetHeight + this.ft.dom.offsetHeight + this.mc.getFrameWidth('tb');
24401         }else{
24402             h += (this.header ? this.header.getHeight() : 0) +
24403                 (this.footer ? this.footer.getHeight() : 0);
24404         }
24405         return h;
24406     },
24407
24408     /**
24409      * Returns the width in pixels of the body element (not including the width of any framing elements).
24410      * For the frame width see {@link #getFrameWidth}.
24411      * @return {Number} The body width
24412      */
24413     getInnerWidth : function(){
24414         return this.getSize().width - this.getFrameWidth();
24415     },
24416
24417     /**
24418      * Returns the height in pixels of the body element (not including the height of any framing elements).
24419      * For the frame height see {@link #getFrameHeight}.
24420      * @return {Number} The body height
24421      */
24422     getInnerHeight : function(){
24423         return this.getSize().height - this.getFrameHeight();
24424     },
24425
24426     // private
24427     syncShadow : function(){
24428         if(this.floating){
24429             this.el.sync(true);
24430         }
24431     },
24432
24433     // private
24434     getLayoutTarget : function(){
24435         return this.body;
24436     },
24437
24438     // private
24439     getContentTarget : function(){
24440         return this.body;
24441     },
24442
24443     /**
24444      * <p>Sets the title text for the panel and optionally the {@link #iconCls icon class}.</p>
24445      * <p>In order to be able to set the title, a header element must have been created
24446      * for the Panel. This is triggered either by configuring the Panel with a non-blank <code>{@link #title}</code>,
24447      * or configuring it with <code><b>{@link #header}: true</b></code>.</p>
24448      * @param {String} title The title text to set
24449      * @param {String} iconCls (optional) {@link #iconCls iconCls} A user-defined CSS class that provides the icon image for this panel
24450      */
24451     setTitle : function(title, iconCls){
24452         this.title = title;
24453         if(this.header && this.headerAsText){
24454             this.header.child('span').update(title);
24455         }
24456         if(iconCls){
24457             this.setIconClass(iconCls);
24458         }
24459         this.fireEvent('titlechange', this, title);
24460         return this;
24461     },
24462
24463     /**
24464      * Get the {@link Ext.Updater} for this panel. Enables you to perform Ajax updates of this panel's body.
24465      * @return {Ext.Updater} The Updater
24466      */
24467     getUpdater : function(){
24468         return this.body.getUpdater();
24469     },
24470
24471      /**
24472      * Loads this content panel immediately with content returned from an XHR call.
24473      * @param {Object/String/Function} config A config object containing any of the following options:
24474 <pre><code>
24475 panel.load({
24476     url: 'your-url.php',
24477     params: {param1: 'foo', param2: 'bar'}, // or a URL encoded string
24478     callback: yourFunction,
24479     scope: yourObject, // optional scope for the callback
24480     discardUrl: false,
24481     nocache: false,
24482     text: 'Loading...',
24483     timeout: 30,
24484     scripts: false
24485 });
24486 </code></pre>
24487      * The only required property is url. The optional properties nocache, text and scripts
24488      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their
24489      * associated property on this panel Updater instance.
24490      * @return {Ext.Panel} this
24491      */
24492     load : function(){
24493         var um = this.body.getUpdater();
24494         um.update.apply(um, arguments);
24495         return this;
24496     },
24497
24498     // private
24499     beforeDestroy : function(){
24500         Ext.Panel.superclass.beforeDestroy.call(this);
24501         if(this.header){
24502             this.header.removeAllListeners();
24503         }
24504         if(this.tools){
24505             for(var k in this.tools){
24506                 Ext.destroy(this.tools[k]);
24507             }
24508         }
24509         if(this.toolbars.length > 0){
24510             Ext.each(this.toolbars, function(tb){
24511                 tb.un('afterlayout', this.syncHeight, this);
24512                 tb.un('remove', this.syncHeight, this);
24513             }, this);
24514         }
24515         if(Ext.isArray(this.buttons)){
24516             while(this.buttons.length) {
24517                 Ext.destroy(this.buttons[0]);
24518             }
24519         }
24520         if(this.rendered){
24521             Ext.destroy(
24522                 this.ft,
24523                 this.header,
24524                 this.footer,
24525                 this.toolbars,
24526                 this.tbar,
24527                 this.bbar,
24528                 this.body,
24529                 this.mc,
24530                 this.bwrap
24531             );
24532             if (this.fbar) {
24533                 Ext.destroy(
24534                     this.fbar,
24535                     this.fbar.el
24536                 );
24537             }
24538         }else{
24539             Ext.destroy(
24540                 this.topToolbar,
24541                 this.bottomToolbar
24542             );
24543         }
24544     },
24545
24546     // private
24547     createClasses : function(){
24548         this.headerCls = this.baseCls + '-header';
24549         this.headerTextCls = this.baseCls + '-header-text';
24550         this.bwrapCls = this.baseCls + '-bwrap';
24551         this.tbarCls = this.baseCls + '-tbar';
24552         this.bodyCls = this.baseCls + '-body';
24553         this.bbarCls = this.baseCls + '-bbar';
24554         this.footerCls = this.baseCls + '-footer';
24555     },
24556
24557     // private
24558     createGhost : function(cls, useShim, appendTo){
24559         var el = document.createElement('div');
24560         el.className = 'x-panel-ghost ' + (cls ? cls : '');
24561         if(this.header){
24562             el.appendChild(this.el.dom.firstChild.cloneNode(true));
24563         }
24564         Ext.fly(el.appendChild(document.createElement('ul'))).setHeight(this.bwrap.getHeight());
24565         el.style.width = this.el.dom.offsetWidth + 'px';;
24566         if(!appendTo){
24567             this.container.dom.appendChild(el);
24568         }else{
24569             Ext.getDom(appendTo).appendChild(el);
24570         }
24571         if(useShim !== false && this.el.useShim !== false){
24572             var layer = new Ext.Layer({shadow:false, useDisplay:true, constrain:false}, el);
24573             layer.show();
24574             return layer;
24575         }else{
24576             return new Ext.Element(el);
24577         }
24578     },
24579
24580     // private
24581     doAutoLoad : function(){
24582         var u = this.body.getUpdater();
24583         if(this.renderer){
24584             u.setRenderer(this.renderer);
24585         }
24586         u.update(Ext.isObject(this.autoLoad) ? this.autoLoad : {url: this.autoLoad});
24587     },
24588
24589     /**
24590      * Retrieve a tool by id.
24591      * @param {String} id
24592      * @return {Object} tool
24593      */
24594     getTool : function(id) {
24595         return this.tools[id];
24596     }
24597
24598 /**
24599  * @cfg {String} autoEl @hide
24600  */
24601 });
24602 Ext.reg('panel', Ext.Panel);
24603 /**
24604  * @class Ext.Editor
24605  * @extends Ext.Component
24606  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
24607  * @constructor
24608  * Create a new Editor
24609  * @param {Object} config The config object
24610  * @xtype editor
24611  */
24612 Ext.Editor = function(field, config){
24613     if(field.field){
24614         this.field = Ext.create(field.field, 'textfield');
24615         config = Ext.apply({}, field); // copy so we don't disturb original config
24616         delete config.field;
24617     }else{
24618         this.field = field;
24619     }
24620     Ext.Editor.superclass.constructor.call(this, config);
24621 };
24622
24623 Ext.extend(Ext.Editor, Ext.Component, {
24624     /**
24625     * @cfg {Ext.form.Field} field
24626     * The Field object (or descendant) or config object for field
24627     */
24628     /**
24629      * @cfg {Boolean} allowBlur
24630      * True to {@link #completeEdit complete the editing process} if in edit mode when the
24631      * field is blurred. Defaults to <tt>false</tt>.
24632      */
24633     /**
24634      * @cfg {Boolean/String} autoSize
24635      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
24636      * or "height" to adopt the height only, "none" to always use the field dimensions. (defaults to false)
24637      */
24638     /**
24639      * @cfg {Boolean} revertInvalid
24640      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
24641      * validation fails (defaults to true)
24642      */
24643     /**
24644      * @cfg {Boolean} ignoreNoChange
24645      * True to skip the edit completion process (no save, no events fired) if the user completes an edit and
24646      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
24647      * will never be ignored.
24648      */
24649     /**
24650      * @cfg {Boolean} hideEl
24651      * False to keep the bound element visible while the editor is displayed (defaults to true)
24652      */
24653     /**
24654      * @cfg {Mixed} value
24655      * The data value of the underlying field (defaults to "")
24656      */
24657     value : "",
24658     /**
24659      * @cfg {String} alignment
24660      * The position to align to (see {@link Ext.Element#alignTo} for more details, defaults to "c-c?").
24661      */
24662     alignment: "c-c?",
24663     /**
24664      * @cfg {Array} offsets
24665      * The offsets to use when aligning (see {@link Ext.Element#alignTo} for more details. Defaults to <tt>[0, 0]</tt>.
24666      */
24667     offsets: [0, 0],
24668     /**
24669      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
24670      * for bottom-right shadow (defaults to "frame")
24671      */
24672     shadow : "frame",
24673     /**
24674      * @cfg {Boolean} constrain True to constrain the editor to the viewport
24675      */
24676     constrain : false,
24677     /**
24678      * @cfg {Boolean} swallowKeys Handle the keydown/keypress events so they don't propagate (defaults to true)
24679      */
24680     swallowKeys : true,
24681     /**
24682      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed. Defaults to <tt>true</tt>.
24683      */
24684     completeOnEnter : true,
24685     /**
24686      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed. Defaults to <tt>true</tt>.
24687      */
24688     cancelOnEsc : true,
24689     /**
24690      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
24691      */
24692     updateEl : false,
24693
24694     initComponent : function(){
24695         Ext.Editor.superclass.initComponent.call(this);
24696         this.addEvents(
24697             /**
24698              * @event beforestartedit
24699              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
24700              * false from the handler of this event.
24701              * @param {Editor} this
24702              * @param {Ext.Element} boundEl The underlying element bound to this editor
24703              * @param {Mixed} value The field value being set
24704              */
24705             "beforestartedit",
24706             /**
24707              * @event startedit
24708              * Fires when this editor is displayed
24709              * @param {Ext.Element} boundEl The underlying element bound to this editor
24710              * @param {Mixed} value The starting field value
24711              */
24712             "startedit",
24713             /**
24714              * @event beforecomplete
24715              * Fires after a change has been made to the field, but before the change is reflected in the underlying
24716              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
24717              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
24718              * event will not fire since no edit actually occurred.
24719              * @param {Editor} this
24720              * @param {Mixed} value The current field value
24721              * @param {Mixed} startValue The original field value
24722              */
24723             "beforecomplete",
24724             /**
24725              * @event complete
24726              * Fires after editing is complete and any changed value has been written to the underlying field.
24727              * @param {Editor} this
24728              * @param {Mixed} value The current field value
24729              * @param {Mixed} startValue The original field value
24730              */
24731             "complete",
24732             /**
24733              * @event canceledit
24734              * Fires after editing has been canceled and the editor's value has been reset.
24735              * @param {Editor} this
24736              * @param {Mixed} value The user-entered field value that was discarded
24737              * @param {Mixed} startValue The original field value that was set back into the editor after cancel
24738              */
24739             "canceledit",
24740             /**
24741              * @event specialkey
24742              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
24743              * {@link Ext.EventObject#getKey} to determine which key was pressed.
24744              * @param {Ext.form.Field} this
24745              * @param {Ext.EventObject} e The event object
24746              */
24747             "specialkey"
24748         );
24749     },
24750
24751     // private
24752     onRender : function(ct, position){
24753         this.el = new Ext.Layer({
24754             shadow: this.shadow,
24755             cls: "x-editor",
24756             parentEl : ct,
24757             shim : this.shim,
24758             shadowOffset: this.shadowOffset || 4,
24759             id: this.id,
24760             constrain: this.constrain
24761         });
24762         if(this.zIndex){
24763             this.el.setZIndex(this.zIndex);
24764         }
24765         this.el.setStyle("overflow", Ext.isGecko ? "auto" : "hidden");
24766         if(this.field.msgTarget != 'title'){
24767             this.field.msgTarget = 'qtip';
24768         }
24769         this.field.inEditor = true;
24770         this.mon(this.field, {
24771             scope: this,
24772             blur: this.onBlur,
24773             specialkey: this.onSpecialKey
24774         });
24775         if(this.field.grow){
24776             this.mon(this.field, "autosize", this.el.sync,  this.el, {delay:1});
24777         }
24778         this.field.render(this.el).show();
24779         this.field.getEl().dom.name = '';
24780         if(this.swallowKeys){
24781             this.field.el.swallowEvent([
24782                 'keypress', // *** Opera
24783                 'keydown'   // *** all other browsers
24784             ]);
24785         }
24786     },
24787
24788     // private
24789     onSpecialKey : function(field, e){
24790         var key = e.getKey(),
24791             complete = this.completeOnEnter && key == e.ENTER,
24792             cancel = this.cancelOnEsc && key == e.ESC;
24793         if(complete || cancel){
24794             e.stopEvent();
24795             if(complete){
24796                 this.completeEdit();
24797             }else{
24798                 this.cancelEdit();
24799             }
24800             if(field.triggerBlur){
24801                 field.triggerBlur();
24802             }
24803         }
24804         this.fireEvent('specialkey', field, e);
24805     },
24806
24807     /**
24808      * Starts the editing process and shows the editor.
24809      * @param {Mixed} el The element to edit
24810      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
24811       * to the innerHTML of el.
24812      */
24813     startEdit : function(el, value){
24814         if(this.editing){
24815             this.completeEdit();
24816         }
24817         this.boundEl = Ext.get(el);
24818         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
24819         if(!this.rendered){
24820             this.render(this.parentEl || document.body);
24821         }
24822         if(this.fireEvent("beforestartedit", this, this.boundEl, v) !== false){
24823             this.startValue = v;
24824             this.field.reset();
24825             this.field.setValue(v);
24826             this.realign(true);
24827             this.editing = true;
24828             this.show();
24829         }
24830     },
24831
24832     // private
24833     doAutoSize : function(){
24834         if(this.autoSize){
24835             var sz = this.boundEl.getSize(),
24836                 fs = this.field.getSize();
24837
24838             switch(this.autoSize){
24839                 case "width":
24840                     this.setSize(sz.width, fs.height);
24841                     break;
24842                 case "height":
24843                     this.setSize(fs.width, sz.height);
24844                     break;
24845                 case "none":
24846                     this.setSize(fs.width, fs.height);
24847                     break;
24848                 default:
24849                     this.setSize(sz.width, sz.height);
24850             }
24851         }
24852     },
24853
24854     /**
24855      * Sets the height and width of this editor.
24856      * @param {Number} width The new width
24857      * @param {Number} height The new height
24858      */
24859     setSize : function(w, h){
24860         delete this.field.lastSize;
24861         this.field.setSize(w, h);
24862         if(this.el){
24863             if(Ext.isGecko2 || Ext.isOpera){
24864                 // prevent layer scrollbars
24865                 this.el.setSize(w, h);
24866             }
24867             this.el.sync();
24868         }
24869     },
24870
24871     /**
24872      * Realigns the editor to the bound field based on the current alignment config value.
24873      * @param {Boolean} autoSize (optional) True to size the field to the dimensions of the bound element.
24874      */
24875     realign : function(autoSize){
24876         if(autoSize === true){
24877             this.doAutoSize();
24878         }
24879         this.el.alignTo(this.boundEl, this.alignment, this.offsets);
24880     },
24881
24882     /**
24883      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
24884      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
24885      */
24886     completeEdit : function(remainVisible){
24887         if(!this.editing){
24888             return;
24889         }
24890         var v = this.getValue();
24891         if(!this.field.isValid()){
24892             if(this.revertInvalid !== false){
24893                 this.cancelEdit(remainVisible);
24894             }
24895             return;
24896         }
24897         if(String(v) === String(this.startValue) && this.ignoreNoChange){
24898             this.hideEdit(remainVisible);
24899             return;
24900         }
24901         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
24902             v = this.getValue();
24903             if(this.updateEl && this.boundEl){
24904                 this.boundEl.update(v);
24905             }
24906             this.hideEdit(remainVisible);
24907             this.fireEvent("complete", this, v, this.startValue);
24908         }
24909     },
24910
24911     // private
24912     onShow : function(){
24913         this.el.show();
24914         if(this.hideEl !== false){
24915             this.boundEl.hide();
24916         }
24917         this.field.show().focus(false, true);
24918         this.fireEvent("startedit", this.boundEl, this.startValue);
24919     },
24920
24921     /**
24922      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
24923      * reverted to the original starting value.
24924      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
24925      * cancel (defaults to false)
24926      */
24927     cancelEdit : function(remainVisible){
24928         if(this.editing){
24929             var v = this.getValue();
24930             this.setValue(this.startValue);
24931             this.hideEdit(remainVisible);
24932             this.fireEvent("canceledit", this, v, this.startValue);
24933         }
24934     },
24935
24936     // private
24937     hideEdit: function(remainVisible){
24938         if(remainVisible !== true){
24939             this.editing = false;
24940             this.hide();
24941         }
24942     },
24943
24944     // private
24945     onBlur : function(){
24946         // selectSameEditor flag allows the same editor to be started without onBlur firing on itself
24947         if(this.allowBlur !== true && this.editing && this.selectSameEditor !== true){
24948             this.completeEdit();
24949         }
24950     },
24951
24952     // private
24953     onHide : function(){
24954         if(this.editing){
24955             this.completeEdit();
24956             return;
24957         }
24958         this.field.blur();
24959         if(this.field.collapse){
24960             this.field.collapse();
24961         }
24962         this.el.hide();
24963         if(this.hideEl !== false){
24964             this.boundEl.show();
24965         }
24966     },
24967
24968     /**
24969      * Sets the data value of the editor
24970      * @param {Mixed} value Any valid value supported by the underlying field
24971      */
24972     setValue : function(v){
24973         this.field.setValue(v);
24974     },
24975
24976     /**
24977      * Gets the data value of the editor
24978      * @return {Mixed} The data value
24979      */
24980     getValue : function(){
24981         return this.field.getValue();
24982     },
24983
24984     beforeDestroy : function(){
24985         Ext.destroyMembers(this, 'field');
24986
24987         delete this.parentEl;
24988         delete this.boundEl;
24989     }
24990 });
24991 Ext.reg('editor', Ext.Editor);
24992 /**
24993  * @class Ext.ColorPalette
24994  * @extends Ext.Component
24995  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
24996  * Here's an example of typical usage:
24997  * <pre><code>
24998 var cp = new Ext.ColorPalette({value:'993300'});  // initial selected color
24999 cp.render('my-div');
25000
25001 cp.on('select', function(palette, selColor){
25002     // do something with selColor
25003 });
25004 </code></pre>
25005  * @constructor
25006  * Create a new ColorPalette
25007  * @param {Object} config The config object
25008  * @xtype colorpalette
25009  */
25010 Ext.ColorPalette = Ext.extend(Ext.Component, {
25011         /**
25012          * @cfg {String} tpl An existing XTemplate instance to be used in place of the default template for rendering the component.
25013          */
25014     /**
25015      * @cfg {String} itemCls
25016      * The CSS class to apply to the containing element (defaults to 'x-color-palette')
25017      */
25018     itemCls : 'x-color-palette',
25019     /**
25020      * @cfg {String} value
25021      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
25022      * the hex codes are case-sensitive.
25023      */
25024     value : null,
25025     /**
25026      * @cfg {String} clickEvent
25027      * The DOM event that will cause a color to be selected. This can be any valid event name (dblclick, contextmenu). 
25028      * Defaults to <tt>'click'</tt>.
25029      */
25030     clickEvent :'click',
25031     // private
25032     ctype : 'Ext.ColorPalette',
25033
25034     /**
25035      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the {@link #select} event
25036      */
25037     allowReselect : false,
25038
25039     /**
25040      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
25041      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
25042      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
25043      * of colors with the width setting until the box is symmetrical.</p>
25044      * <p>You can override individual colors if needed:</p>
25045      * <pre><code>
25046 var cp = new Ext.ColorPalette();
25047 cp.colors[0] = 'FF0000';  // change the first box to red
25048 </code></pre>
25049
25050 Or you can provide a custom array of your own for complete control:
25051 <pre><code>
25052 var cp = new Ext.ColorPalette();
25053 cp.colors = ['000000', '993300', '333300'];
25054 </code></pre>
25055      * @type Array
25056      */
25057     colors : [
25058         '000000', '993300', '333300', '003300', '003366', '000080', '333399', '333333',
25059         '800000', 'FF6600', '808000', '008000', '008080', '0000FF', '666699', '808080',
25060         'FF0000', 'FF9900', '99CC00', '339966', '33CCCC', '3366FF', '800080', '969696',
25061         'FF00FF', 'FFCC00', 'FFFF00', '00FF00', '00FFFF', '00CCFF', '993366', 'C0C0C0',
25062         'FF99CC', 'FFCC99', 'FFFF99', 'CCFFCC', 'CCFFFF', '99CCFF', 'CC99FF', 'FFFFFF'
25063     ],
25064
25065     /**
25066      * @cfg {Function} handler
25067      * Optional. A function that will handle the select event of this palette.
25068      * The handler is passed the following parameters:<div class="mdetail-params"><ul>
25069      * <li><code>palette</code> : ColorPalette<div class="sub-desc">The {@link #palette Ext.ColorPalette}.</div></li>
25070      * <li><code>color</code> : String<div class="sub-desc">The 6-digit color hex code (without the # symbol).</div></li>
25071      * </ul></div>
25072      */
25073     /**
25074      * @cfg {Object} scope
25075      * The scope (<tt><b>this</b></tt> reference) in which the <code>{@link #handler}</code>
25076      * function will be called.  Defaults to this ColorPalette instance.
25077      */
25078     
25079     // private
25080     initComponent : function(){
25081         Ext.ColorPalette.superclass.initComponent.call(this);
25082         this.addEvents(
25083             /**
25084              * @event select
25085              * Fires when a color is selected
25086              * @param {ColorPalette} this
25087              * @param {String} color The 6-digit color hex code (without the # symbol)
25088              */
25089             'select'
25090         );
25091
25092         if(this.handler){
25093             this.on('select', this.handler, this.scope, true);
25094         }    
25095     },
25096
25097     // private
25098     onRender : function(container, position){
25099         this.autoEl = {
25100             tag: 'div',
25101             cls: this.itemCls
25102         };
25103         Ext.ColorPalette.superclass.onRender.call(this, container, position);
25104         var t = this.tpl || new Ext.XTemplate(
25105             '<tpl for="."><a href="#" class="color-{.}" hidefocus="on"><em><span style="background:#{.}" unselectable="on">&#160;</span></em></a></tpl>'
25106         );
25107         t.overwrite(this.el, this.colors);
25108         this.mon(this.el, this.clickEvent, this.handleClick, this, {delegate: 'a'});
25109         if(this.clickEvent != 'click'){
25110                 this.mon(this.el, 'click', Ext.emptyFn, this, {delegate: 'a', preventDefault: true});
25111         }
25112     },
25113
25114     // private
25115     afterRender : function(){
25116         Ext.ColorPalette.superclass.afterRender.call(this);
25117         if(this.value){
25118             var s = this.value;
25119             this.value = null;
25120             this.select(s);
25121         }
25122     },
25123
25124     // private
25125     handleClick : function(e, t){
25126         e.preventDefault();
25127         if(!this.disabled){
25128             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
25129             this.select(c.toUpperCase());
25130         }
25131     },
25132
25133     /**
25134      * Selects the specified color in the palette (fires the {@link #select} event)
25135      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
25136      */
25137     select : function(color){
25138         color = color.replace('#', '');
25139         if(color != this.value || this.allowReselect){
25140             var el = this.el;
25141             if(this.value){
25142                 el.child('a.color-'+this.value).removeClass('x-color-palette-sel');
25143             }
25144             el.child('a.color-'+color).addClass('x-color-palette-sel');
25145             this.value = color;
25146             this.fireEvent('select', this, color);
25147         }
25148     }
25149
25150     /**
25151      * @cfg {String} autoEl @hide
25152      */
25153 });
25154 Ext.reg('colorpalette', Ext.ColorPalette);
25155 /**
25156  * @class Ext.DatePicker
25157  * @extends Ext.Component
25158  * <p>A popup date picker. This class is used by the {@link Ext.form.DateField DateField} class
25159  * to allow browsing and selection of valid dates.</p>
25160  * <p>All the string values documented below may be overridden by including an Ext locale file in
25161  * your page.</p>
25162  * @constructor
25163  * Create a new DatePicker
25164  * @param {Object} config The config object
25165  * @xtype datepicker
25166  */
25167 Ext.DatePicker = Ext.extend(Ext.BoxComponent, {
25168     /**
25169      * @cfg {String} todayText
25170      * The text to display on the button that selects the current date (defaults to <code>'Today'</code>)
25171      */
25172     todayText : 'Today',
25173     /**
25174      * @cfg {String} okText
25175      * The text to display on the ok button (defaults to <code>'&#160;OK&#160;'</code> to give the user extra clicking room)
25176      */
25177     okText : '&#160;OK&#160;',
25178     /**
25179      * @cfg {String} cancelText
25180      * The text to display on the cancel button (defaults to <code>'Cancel'</code>)
25181      */
25182     cancelText : 'Cancel',
25183     /**
25184      * @cfg {Function} handler
25185      * Optional. A function that will handle the select event of this picker.
25186      * The handler is passed the following parameters:<div class="mdetail-params"><ul>
25187      * <li><code>picker</code> : DatePicker<div class="sub-desc">This DatePicker.</div></li>
25188      * <li><code>date</code> : Date<div class="sub-desc">The selected date.</div></li>
25189      * </ul></div>
25190      */
25191     /**
25192      * @cfg {Object} scope
25193      * The scope (<code><b>this</b></code> reference) in which the <code>{@link #handler}</code>
25194      * function will be called.  Defaults to this DatePicker instance.
25195      */ 
25196     /**
25197      * @cfg {String} todayTip
25198      * A string used to format the message for displaying in a tooltip over the button that
25199      * selects the current date. Defaults to <code>'{0} (Spacebar)'</code> where
25200      * the <code>{0}</code> token is replaced by today's date.
25201      */
25202     todayTip : '{0} (Spacebar)',
25203     /**
25204      * @cfg {String} minText
25205      * The error text to display if the minDate validation fails (defaults to <code>'This date is before the minimum date'</code>)
25206      */
25207     minText : 'This date is before the minimum date',
25208     /**
25209      * @cfg {String} maxText
25210      * The error text to display if the maxDate validation fails (defaults to <code>'This date is after the maximum date'</code>)
25211      */
25212     maxText : 'This date is after the maximum date',
25213     /**
25214      * @cfg {String} format
25215      * The default date format string which can be overriden for localization support.  The format must be
25216      * valid according to {@link Date#parseDate} (defaults to <code>'m/d/y'</code>).
25217      */
25218     format : 'm/d/y',
25219     /**
25220      * @cfg {String} disabledDaysText
25221      * The tooltip to display when the date falls on a disabled day (defaults to <code>'Disabled'</code>)
25222      */
25223     disabledDaysText : 'Disabled',
25224     /**
25225      * @cfg {String} disabledDatesText
25226      * The tooltip text to display when the date falls on a disabled date (defaults to <code>'Disabled'</code>)
25227      */
25228     disabledDatesText : 'Disabled',
25229     /**
25230      * @cfg {Array} monthNames
25231      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
25232      */
25233     monthNames : Date.monthNames,
25234     /**
25235      * @cfg {Array} dayNames
25236      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
25237      */
25238     dayNames : Date.dayNames,
25239     /**
25240      * @cfg {String} nextText
25241      * The next month navigation button tooltip (defaults to <code>'Next Month (Control+Right)'</code>)
25242      */
25243     nextText : 'Next Month (Control+Right)',
25244     /**
25245      * @cfg {String} prevText
25246      * The previous month navigation button tooltip (defaults to <code>'Previous Month (Control+Left)'</code>)
25247      */
25248     prevText : 'Previous Month (Control+Left)',
25249     /**
25250      * @cfg {String} monthYearText
25251      * The header month selector tooltip (defaults to <code>'Choose a month (Control+Up/Down to move years)'</code>)
25252      */
25253     monthYearText : 'Choose a month (Control+Up/Down to move years)',
25254     /**
25255      * @cfg {Number} startDay
25256      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
25257      */
25258     startDay : 0,
25259     /**
25260      * @cfg {Boolean} showToday
25261      * False to hide the footer area containing the Today button and disable the keyboard handler for spacebar
25262      * that selects the current date (defaults to <code>true</code>).
25263      */
25264     showToday : true,
25265     /**
25266      * @cfg {Date} minDate
25267      * Minimum allowable date (JavaScript date object, defaults to null)
25268      */
25269     /**
25270      * @cfg {Date} maxDate
25271      * Maximum allowable date (JavaScript date object, defaults to null)
25272      */
25273     /**
25274      * @cfg {Array} disabledDays
25275      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
25276      */
25277     /**
25278      * @cfg {RegExp} disabledDatesRE
25279      * JavaScript regular expression used to disable a pattern of dates (defaults to null).  The {@link #disabledDates}
25280      * config will generate this regex internally, but if you specify disabledDatesRE it will take precedence over the
25281      * disabledDates value.
25282      */
25283     /**
25284      * @cfg {Array} disabledDates
25285      * An array of 'dates' to disable, as strings. These strings will be used to build a dynamic regular
25286      * expression so they are very powerful. Some examples:
25287      * <ul>
25288      * <li>['03/08/2003', '09/16/2003'] would disable those exact dates</li>
25289      * <li>['03/08', '09/16'] would disable those days for every year</li>
25290      * <li>['^03/08'] would only match the beginning (useful if you are using short years)</li>
25291      * <li>['03/../2006'] would disable every day in March 2006</li>
25292      * <li>['^03'] would disable every day in every March</li>
25293      * </ul>
25294      * Note that the format of the dates included in the array should exactly match the {@link #format} config.
25295      * In order to support regular expressions, if you are using a date format that has '.' in it, you will have to
25296      * escape the dot when restricting dates. For example: ['03\\.08\\.03'].
25297      */
25298     
25299     // private
25300     // Set by other components to stop the picker focus being updated when the value changes.
25301     focusOnSelect: true,
25302
25303     // private
25304     initComponent : function(){
25305         Ext.DatePicker.superclass.initComponent.call(this);
25306
25307         this.value = this.value ?
25308                  this.value.clearTime(true) : new Date().clearTime();
25309
25310         this.addEvents(
25311             /**
25312              * @event select
25313              * Fires when a date is selected
25314              * @param {DatePicker} this DatePicker
25315              * @param {Date} date The selected date
25316              */
25317             'select'
25318         );
25319
25320         if(this.handler){
25321             this.on('select', this.handler,  this.scope || this);
25322         }
25323
25324         this.initDisabledDays();
25325     },
25326
25327     // private
25328     initDisabledDays : function(){
25329         if(!this.disabledDatesRE && this.disabledDates){
25330             var dd = this.disabledDates,
25331                 len = dd.length - 1,
25332                 re = '(?:';
25333                 
25334             Ext.each(dd, function(d, i){
25335                 re += Ext.isDate(d) ? '^' + Ext.escapeRe(d.dateFormat(this.format)) + '$' : dd[i];
25336                 if(i != len){
25337                     re += '|';
25338                 }
25339             }, this);
25340             this.disabledDatesRE = new RegExp(re + ')');
25341         }
25342     },
25343
25344     /**
25345      * Replaces any existing disabled dates with new values and refreshes the DatePicker.
25346      * @param {Array/RegExp} disabledDates An array of date strings (see the {@link #disabledDates} config
25347      * for details on supported values), or a JavaScript regular expression used to disable a pattern of dates.
25348      */
25349     setDisabledDates : function(dd){
25350         if(Ext.isArray(dd)){
25351             this.disabledDates = dd;
25352             this.disabledDatesRE = null;
25353         }else{
25354             this.disabledDatesRE = dd;
25355         }
25356         this.initDisabledDays();
25357         this.update(this.value, true);
25358     },
25359
25360     /**
25361      * Replaces any existing disabled days (by index, 0-6) with new values and refreshes the DatePicker.
25362      * @param {Array} disabledDays An array of disabled day indexes. See the {@link #disabledDays} config
25363      * for details on supported values.
25364      */
25365     setDisabledDays : function(dd){
25366         this.disabledDays = dd;
25367         this.update(this.value, true);
25368     },
25369
25370     /**
25371      * Replaces any existing {@link #minDate} with the new value and refreshes the DatePicker.
25372      * @param {Date} value The minimum date that can be selected
25373      */
25374     setMinDate : function(dt){
25375         this.minDate = dt;
25376         this.update(this.value, true);
25377     },
25378
25379     /**
25380      * Replaces any existing {@link #maxDate} with the new value and refreshes the DatePicker.
25381      * @param {Date} value The maximum date that can be selected
25382      */
25383     setMaxDate : function(dt){
25384         this.maxDate = dt;
25385         this.update(this.value, true);
25386     },
25387
25388     /**
25389      * Sets the value of the date field
25390      * @param {Date} value The date to set
25391      */
25392     setValue : function(value){
25393         this.value = value.clearTime(true);
25394         this.update(this.value);
25395     },
25396
25397     /**
25398      * Gets the current selected value of the date field
25399      * @return {Date} The selected date
25400      */
25401     getValue : function(){
25402         return this.value;
25403     },
25404
25405     // private
25406     focus : function(){
25407         this.update(this.activeDate);
25408     },
25409     
25410     // private
25411     onEnable: function(initial){
25412         Ext.DatePicker.superclass.onEnable.call(this);    
25413         this.doDisabled(false);
25414         this.update(initial ? this.value : this.activeDate);
25415         if(Ext.isIE){
25416             this.el.repaint();
25417         }
25418         
25419     },
25420     
25421     // private
25422     onDisable : function(){
25423         Ext.DatePicker.superclass.onDisable.call(this);   
25424         this.doDisabled(true);
25425         if(Ext.isIE && !Ext.isIE8){
25426             /* Really strange problem in IE6/7, when disabled, have to explicitly
25427              * repaint each of the nodes to get them to display correctly, simply
25428              * calling repaint on the main element doesn't appear to be enough.
25429              */
25430              Ext.each([].concat(this.textNodes, this.el.query('th span')), function(el){
25431                  Ext.fly(el).repaint();
25432              });
25433         }
25434     },
25435     
25436     // private
25437     doDisabled : function(disabled){
25438         this.keyNav.setDisabled(disabled);
25439         this.prevRepeater.setDisabled(disabled);
25440         this.nextRepeater.setDisabled(disabled);
25441         if(this.showToday){
25442             this.todayKeyListener.setDisabled(disabled);
25443             this.todayBtn.setDisabled(disabled);
25444         }
25445     },
25446
25447     // private
25448     onRender : function(container, position){
25449         var m = [
25450              '<table cellspacing="0">',
25451                 '<tr><td class="x-date-left"><a href="#" title="', this.prevText ,'">&#160;</a></td><td class="x-date-middle" align="center"></td><td class="x-date-right"><a href="#" title="', this.nextText ,'">&#160;</a></td></tr>',
25452                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'],
25453                 dn = this.dayNames,
25454                 i;
25455         for(i = 0; i < 7; i++){
25456             var d = this.startDay+i;
25457             if(d > 6){
25458                 d = d-7;
25459             }
25460             m.push('<th><span>', dn[d].substr(0,1), '</span></th>');
25461         }
25462         m[m.length] = '</tr></thead><tbody><tr>';
25463         for(i = 0; i < 42; i++) {
25464             if(i % 7 === 0 && i !== 0){
25465                 m[m.length] = '</tr><tr>';
25466             }
25467             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
25468         }
25469         m.push('</tr></tbody></table></td></tr>',
25470                 this.showToday ? '<tr><td colspan="3" class="x-date-bottom" align="center"></td></tr>' : '',
25471                 '</table><div class="x-date-mp"></div>');
25472
25473         var el = document.createElement('div');
25474         el.className = 'x-date-picker';
25475         el.innerHTML = m.join('');
25476
25477         container.dom.insertBefore(el, position);
25478
25479         this.el = Ext.get(el);
25480         this.eventEl = Ext.get(el.firstChild);
25481
25482         this.prevRepeater = new Ext.util.ClickRepeater(this.el.child('td.x-date-left a'), {
25483             handler: this.showPrevMonth,
25484             scope: this,
25485             preventDefault:true,
25486             stopDefault:true
25487         });
25488
25489         this.nextRepeater = new Ext.util.ClickRepeater(this.el.child('td.x-date-right a'), {
25490             handler: this.showNextMonth,
25491             scope: this,
25492             preventDefault:true,
25493             stopDefault:true
25494         });
25495
25496         this.monthPicker = this.el.down('div.x-date-mp');
25497         this.monthPicker.enableDisplayMode('block');
25498
25499         this.keyNav = new Ext.KeyNav(this.eventEl, {
25500             'left' : function(e){
25501                 if(e.ctrlKey){
25502                     this.showPrevMonth();
25503                 }else{
25504                     this.update(this.activeDate.add('d', -1));    
25505                 }
25506             },
25507
25508             'right' : function(e){
25509                 if(e.ctrlKey){
25510                     this.showNextMonth();
25511                 }else{
25512                     this.update(this.activeDate.add('d', 1));    
25513                 }
25514             },
25515
25516             'up' : function(e){
25517                 if(e.ctrlKey){
25518                     this.showNextYear();
25519                 }else{
25520                     this.update(this.activeDate.add('d', -7));
25521                 }
25522             },
25523
25524             'down' : function(e){
25525                 if(e.ctrlKey){
25526                     this.showPrevYear();
25527                 }else{
25528                     this.update(this.activeDate.add('d', 7));
25529                 }
25530             },
25531
25532             'pageUp' : function(e){
25533                 this.showNextMonth();
25534             },
25535
25536             'pageDown' : function(e){
25537                 this.showPrevMonth();
25538             },
25539
25540             'enter' : function(e){
25541                 e.stopPropagation();
25542                 return true;
25543             },
25544
25545             scope : this
25546         });
25547
25548         this.el.unselectable();
25549
25550         this.cells = this.el.select('table.x-date-inner tbody td');
25551         this.textNodes = this.el.query('table.x-date-inner tbody span');
25552
25553         this.mbtn = new Ext.Button({
25554             text: '&#160;',
25555             tooltip: this.monthYearText,
25556             renderTo: this.el.child('td.x-date-middle', true)
25557         });
25558         this.mbtn.el.child('em').addClass('x-btn-arrow');
25559
25560         if(this.showToday){
25561             this.todayKeyListener = this.eventEl.addKeyListener(Ext.EventObject.SPACE, this.selectToday,  this);
25562             var today = (new Date()).dateFormat(this.format);
25563             this.todayBtn = new Ext.Button({
25564                 renderTo: this.el.child('td.x-date-bottom', true),
25565                 text: String.format(this.todayText, today),
25566                 tooltip: String.format(this.todayTip, today),
25567                 handler: this.selectToday,
25568                 scope: this
25569             });
25570         }
25571         this.mon(this.eventEl, 'mousewheel', this.handleMouseWheel, this);
25572         this.mon(this.eventEl, 'click', this.handleDateClick,  this, {delegate: 'a.x-date-date'});
25573         this.mon(this.mbtn, 'click', this.showMonthPicker, this);
25574         this.onEnable(true);
25575     },
25576
25577     // private
25578     createMonthPicker : function(){
25579         if(!this.monthPicker.dom.firstChild){
25580             var buf = ['<table border="0" cellspacing="0">'];
25581             for(var i = 0; i < 6; i++){
25582                 buf.push(
25583                     '<tr><td class="x-date-mp-month"><a href="#">', Date.getShortMonthName(i), '</a></td>',
25584                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', Date.getShortMonthName(i + 6), '</a></td>',
25585                     i === 0 ?
25586                     '<td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-prev"></a></td><td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-next"></a></td></tr>' :
25587                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
25588                 );
25589             }
25590             buf.push(
25591                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
25592                     this.okText,
25593                     '</button><button type="button" class="x-date-mp-cancel">',
25594                     this.cancelText,
25595                     '</button></td></tr>',
25596                 '</table>'
25597             );
25598             this.monthPicker.update(buf.join(''));
25599
25600             this.mon(this.monthPicker, 'click', this.onMonthClick, this);
25601             this.mon(this.monthPicker, 'dblclick', this.onMonthDblClick, this);
25602
25603             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
25604             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
25605
25606             this.mpMonths.each(function(m, a, i){
25607                 i += 1;
25608                 if((i%2) === 0){
25609                     m.dom.xmonth = 5 + Math.round(i * 0.5);
25610                 }else{
25611                     m.dom.xmonth = Math.round((i-1) * 0.5);
25612                 }
25613             });
25614         }
25615     },
25616
25617     // private
25618     showMonthPicker : function(){
25619         if(!this.disabled){
25620             this.createMonthPicker();
25621             var size = this.el.getSize();
25622             this.monthPicker.setSize(size);
25623             this.monthPicker.child('table').setSize(size);
25624
25625             this.mpSelMonth = (this.activeDate || this.value).getMonth();
25626             this.updateMPMonth(this.mpSelMonth);
25627             this.mpSelYear = (this.activeDate || this.value).getFullYear();
25628             this.updateMPYear(this.mpSelYear);
25629
25630             this.monthPicker.slideIn('t', {duration:0.2});
25631         }
25632     },
25633
25634     // private
25635     updateMPYear : function(y){
25636         this.mpyear = y;
25637         var ys = this.mpYears.elements;
25638         for(var i = 1; i <= 10; i++){
25639             var td = ys[i-1], y2;
25640             if((i%2) === 0){
25641                 y2 = y + Math.round(i * 0.5);
25642                 td.firstChild.innerHTML = y2;
25643                 td.xyear = y2;
25644             }else{
25645                 y2 = y - (5-Math.round(i * 0.5));
25646                 td.firstChild.innerHTML = y2;
25647                 td.xyear = y2;
25648             }
25649             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
25650         }
25651     },
25652
25653     // private
25654     updateMPMonth : function(sm){
25655         this.mpMonths.each(function(m, a, i){
25656             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
25657         });
25658     },
25659
25660     // private
25661     selectMPMonth : function(m){
25662
25663     },
25664
25665     // private
25666     onMonthClick : function(e, t){
25667         e.stopEvent();
25668         var el = new Ext.Element(t), pn;
25669         if(el.is('button.x-date-mp-cancel')){
25670             this.hideMonthPicker();
25671         }
25672         else if(el.is('button.x-date-mp-ok')){
25673             var d = new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate());
25674             if(d.getMonth() != this.mpSelMonth){
25675                 // 'fix' the JS rolling date conversion if needed
25676                 d = new Date(this.mpSelYear, this.mpSelMonth, 1).getLastDateOfMonth();
25677             }
25678             this.update(d);
25679             this.hideMonthPicker();
25680         }
25681         else if((pn = el.up('td.x-date-mp-month', 2))){
25682             this.mpMonths.removeClass('x-date-mp-sel');
25683             pn.addClass('x-date-mp-sel');
25684             this.mpSelMonth = pn.dom.xmonth;
25685         }
25686         else if((pn = el.up('td.x-date-mp-year', 2))){
25687             this.mpYears.removeClass('x-date-mp-sel');
25688             pn.addClass('x-date-mp-sel');
25689             this.mpSelYear = pn.dom.xyear;
25690         }
25691         else if(el.is('a.x-date-mp-prev')){
25692             this.updateMPYear(this.mpyear-10);
25693         }
25694         else if(el.is('a.x-date-mp-next')){
25695             this.updateMPYear(this.mpyear+10);
25696         }
25697     },
25698
25699     // private
25700     onMonthDblClick : function(e, t){
25701         e.stopEvent();
25702         var el = new Ext.Element(t), pn;
25703         if((pn = el.up('td.x-date-mp-month', 2))){
25704             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
25705             this.hideMonthPicker();
25706         }
25707         else if((pn = el.up('td.x-date-mp-year', 2))){
25708             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
25709             this.hideMonthPicker();
25710         }
25711     },
25712
25713     // private
25714     hideMonthPicker : function(disableAnim){
25715         if(this.monthPicker){
25716             if(disableAnim === true){
25717                 this.monthPicker.hide();
25718             }else{
25719                 this.monthPicker.slideOut('t', {duration:0.2});
25720             }
25721         }
25722     },
25723
25724     // private
25725     showPrevMonth : function(e){
25726         this.update(this.activeDate.add('mo', -1));
25727     },
25728
25729     // private
25730     showNextMonth : function(e){
25731         this.update(this.activeDate.add('mo', 1));
25732     },
25733
25734     // private
25735     showPrevYear : function(){
25736         this.update(this.activeDate.add('y', -1));
25737     },
25738
25739     // private
25740     showNextYear : function(){
25741         this.update(this.activeDate.add('y', 1));
25742     },
25743
25744     // private
25745     handleMouseWheel : function(e){
25746         e.stopEvent();
25747         if(!this.disabled){
25748             var delta = e.getWheelDelta();
25749             if(delta > 0){
25750                 this.showPrevMonth();
25751             } else if(delta < 0){
25752                 this.showNextMonth();
25753             }
25754         }
25755     },
25756
25757     // private
25758     handleDateClick : function(e, t){
25759         e.stopEvent();
25760         if(!this.disabled && t.dateValue && !Ext.fly(t.parentNode).hasClass('x-date-disabled')){
25761             this.cancelFocus = this.focusOnSelect === false;
25762             this.setValue(new Date(t.dateValue));
25763             delete this.cancelFocus;
25764             this.fireEvent('select', this, this.value);
25765         }
25766     },
25767
25768     // private
25769     selectToday : function(){
25770         if(this.todayBtn && !this.todayBtn.disabled){
25771             this.setValue(new Date().clearTime());
25772             this.fireEvent('select', this, this.value);
25773         }
25774     },
25775
25776     // private
25777     update : function(date, forceRefresh){
25778         if(this.rendered){
25779                 var vd = this.activeDate, vis = this.isVisible();
25780                 this.activeDate = date;
25781                 if(!forceRefresh && vd && this.el){
25782                     var t = date.getTime();
25783                     if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
25784                         this.cells.removeClass('x-date-selected');
25785                         this.cells.each(function(c){
25786                            if(c.dom.firstChild.dateValue == t){
25787                                c.addClass('x-date-selected');
25788                                if(vis && !this.cancelFocus){
25789                                    Ext.fly(c.dom.firstChild).focus(50);
25790                                }
25791                                return false;
25792                            }
25793                         }, this);
25794                         return;
25795                     }
25796                 }
25797                 var days = date.getDaysInMonth(),
25798                     firstOfMonth = date.getFirstDateOfMonth(),
25799                     startingPos = firstOfMonth.getDay()-this.startDay;
25800         
25801                 if(startingPos < 0){
25802                     startingPos += 7;
25803                 }
25804                 days += startingPos;
25805         
25806                 var pm = date.add('mo', -1),
25807                     prevStart = pm.getDaysInMonth()-startingPos,
25808                     cells = this.cells.elements,
25809                     textEls = this.textNodes,
25810                     // convert everything to numbers so it's fast
25811                     day = 86400000,
25812                     d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime(),
25813                     today = new Date().clearTime().getTime(),
25814                     sel = date.clearTime(true).getTime(),
25815                     min = this.minDate ? this.minDate.clearTime(true) : Number.NEGATIVE_INFINITY,
25816                     max = this.maxDate ? this.maxDate.clearTime(true) : Number.POSITIVE_INFINITY,
25817                     ddMatch = this.disabledDatesRE,
25818                     ddText = this.disabledDatesText,
25819                     ddays = this.disabledDays ? this.disabledDays.join('') : false,
25820                     ddaysText = this.disabledDaysText,
25821                     format = this.format;
25822         
25823                 if(this.showToday){
25824                     var td = new Date().clearTime(),
25825                         disable = (td < min || td > max ||
25826                         (ddMatch && format && ddMatch.test(td.dateFormat(format))) ||
25827                         (ddays && ddays.indexOf(td.getDay()) != -1));
25828         
25829                     if(!this.disabled){
25830                         this.todayBtn.setDisabled(disable);
25831                         this.todayKeyListener[disable ? 'disable' : 'enable']();
25832                     }
25833                 }
25834         
25835                 var setCellClass = function(cal, cell){
25836                     cell.title = '';
25837                     var t = d.getTime();
25838                     cell.firstChild.dateValue = t;
25839                     if(t == today){
25840                         cell.className += ' x-date-today';
25841                         cell.title = cal.todayText;
25842                     }
25843                     if(t == sel){
25844                         cell.className += ' x-date-selected';
25845                         if(vis){
25846                             Ext.fly(cell.firstChild).focus(50);
25847                         }
25848                     }
25849                     // disabling
25850                     if(t < min) {
25851                         cell.className = ' x-date-disabled';
25852                         cell.title = cal.minText;
25853                         return;
25854                     }
25855                     if(t > max) {
25856                         cell.className = ' x-date-disabled';
25857                         cell.title = cal.maxText;
25858                         return;
25859                     }
25860                     if(ddays){
25861                         if(ddays.indexOf(d.getDay()) != -1){
25862                             cell.title = ddaysText;
25863                             cell.className = ' x-date-disabled';
25864                         }
25865                     }
25866                     if(ddMatch && format){
25867                         var fvalue = d.dateFormat(format);
25868                         if(ddMatch.test(fvalue)){
25869                             cell.title = ddText.replace('%0', fvalue);
25870                             cell.className = ' x-date-disabled';
25871                         }
25872                     }
25873                 };
25874         
25875                 var i = 0;
25876                 for(; i < startingPos; i++) {
25877                     textEls[i].innerHTML = (++prevStart);
25878                     d.setDate(d.getDate()+1);
25879                     cells[i].className = 'x-date-prevday';
25880                     setCellClass(this, cells[i]);
25881                 }
25882                 for(; i < days; i++){
25883                     var intDay = i - startingPos + 1;
25884                     textEls[i].innerHTML = (intDay);
25885                     d.setDate(d.getDate()+1);
25886                     cells[i].className = 'x-date-active';
25887                     setCellClass(this, cells[i]);
25888                 }
25889                 var extraDays = 0;
25890                 for(; i < 42; i++) {
25891                      textEls[i].innerHTML = (++extraDays);
25892                      d.setDate(d.getDate()+1);
25893                      cells[i].className = 'x-date-nextday';
25894                      setCellClass(this, cells[i]);
25895                 }
25896         
25897                 this.mbtn.setText(this.monthNames[date.getMonth()] + ' ' + date.getFullYear());
25898         
25899                 if(!this.internalRender){
25900                     var main = this.el.dom.firstChild,
25901                         w = main.offsetWidth;
25902                     this.el.setWidth(w + this.el.getBorderWidth('lr'));
25903                     Ext.fly(main).setWidth(w);
25904                     this.internalRender = true;
25905                     // opera does not respect the auto grow header center column
25906                     // then, after it gets a width opera refuses to recalculate
25907                     // without a second pass
25908                     if(Ext.isOpera && !this.secondPass){
25909                         main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + 'px';
25910                         this.secondPass = true;
25911                         this.update.defer(10, this, [date]);
25912                     }
25913                 }
25914         }
25915     },
25916
25917     // private
25918     beforeDestroy : function() {
25919         if(this.rendered){
25920             Ext.destroy(
25921                 this.keyNav,
25922                 this.monthPicker,
25923                 this.eventEl,
25924                 this.mbtn,
25925                 this.nextRepeater,
25926                 this.prevRepeater,
25927                 this.cells.el,
25928                 this.todayBtn
25929             );
25930             delete this.textNodes;
25931             delete this.cells.elements;
25932         }
25933     }
25934
25935     /**
25936      * @cfg {String} autoEl @hide
25937      */
25938 });
25939
25940 Ext.reg('datepicker', Ext.DatePicker);
25941 /**
25942  * @class Ext.LoadMask
25943  * A simple utility class for generically masking elements while loading data.  If the {@link #store}
25944  * config option is specified, the masking will be automatically synchronized with the store's loading
25945  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
25946  * element's Updater load indicator and will be destroyed after the initial load.
25947  * <p>Example usage:</p>
25948  *<pre><code>
25949 // Basic mask:
25950 var myMask = new Ext.LoadMask(Ext.getBody(), {msg:"Please wait..."});
25951 myMask.show();
25952 </code></pre>
25953  * @constructor
25954  * Create a new LoadMask
25955  * @param {Mixed} el The element or DOM node, or its id
25956  * @param {Object} config The config object
25957  */
25958 Ext.LoadMask = function(el, config){
25959     this.el = Ext.get(el);
25960     Ext.apply(this, config);
25961     if(this.store){
25962         this.store.on({
25963             scope: this,
25964             beforeload: this.onBeforeLoad,
25965             load: this.onLoad,
25966             exception: this.onLoad
25967         });
25968         this.removeMask = Ext.value(this.removeMask, false);
25969     }else{
25970         var um = this.el.getUpdater();
25971         um.showLoadIndicator = false; // disable the default indicator
25972         um.on({
25973             scope: this,
25974             beforeupdate: this.onBeforeLoad,
25975             update: this.onLoad,
25976             failure: this.onLoad
25977         });
25978         this.removeMask = Ext.value(this.removeMask, true);
25979     }
25980 };
25981
25982 Ext.LoadMask.prototype = {
25983     /**
25984      * @cfg {Ext.data.Store} store
25985      * Optional Store to which the mask is bound. The mask is displayed when a load request is issued, and
25986      * hidden on either load sucess, or load fail.
25987      */
25988     /**
25989      * @cfg {Boolean} removeMask
25990      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
25991      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
25992      */
25993     /**
25994      * @cfg {String} msg
25995      * The text to display in a centered loading message box (defaults to 'Loading...')
25996      */
25997     msg : 'Loading...',
25998     /**
25999      * @cfg {String} msgCls
26000      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
26001      */
26002     msgCls : 'x-mask-loading',
26003
26004     /**
26005      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
26006      * @type Boolean
26007      */
26008     disabled: false,
26009
26010     /**
26011      * Disables the mask to prevent it from being displayed
26012      */
26013     disable : function(){
26014        this.disabled = true;
26015     },
26016
26017     /**
26018      * Enables the mask so that it can be displayed
26019      */
26020     enable : function(){
26021         this.disabled = false;
26022     },
26023
26024     // private
26025     onLoad : function(){
26026         this.el.unmask(this.removeMask);
26027     },
26028
26029     // private
26030     onBeforeLoad : function(){
26031         if(!this.disabled){
26032             this.el.mask(this.msg, this.msgCls);
26033         }
26034     },
26035
26036     /**
26037      * Show this LoadMask over the configured Element.
26038      */
26039     show: function(){
26040         this.onBeforeLoad();
26041     },
26042
26043     /**
26044      * Hide this LoadMask.
26045      */
26046     hide: function(){
26047         this.onLoad();
26048     },
26049
26050     // private
26051     destroy : function(){
26052         if(this.store){
26053             this.store.un('beforeload', this.onBeforeLoad, this);
26054             this.store.un('load', this.onLoad, this);
26055             this.store.un('exception', this.onLoad, this);
26056         }else{
26057             var um = this.el.getUpdater();
26058             um.un('beforeupdate', this.onBeforeLoad, this);
26059             um.un('update', this.onLoad, this);
26060             um.un('failure', this.onLoad, this);
26061         }
26062     }
26063 };/**\r
26064  * @class Ext.Slider\r
26065  * @extends Ext.BoxComponent\r
26066  * Slider which supports vertical or horizontal orientation, keyboard adjustments,\r
26067  * configurable snapping, axis clicking and animation. Can be added as an item to\r
26068  * any container. Example usage:\r
26069 <pre><code>\r
26070 new Ext.Slider({\r
26071     renderTo: Ext.getBody(),\r
26072     width: 200,\r
26073     value: 50,\r
26074     increment: 10,\r
26075     minValue: 0,\r
26076     maxValue: 100\r
26077 });\r
26078 </code></pre>\r
26079  */\r
26080 Ext.Slider = Ext.extend(Ext.BoxComponent, {\r
26081     /**\r
26082      * @cfg {Number} value The value to initialize the slider with. Defaults to minValue.\r
26083      */\r
26084     /**\r
26085      * @cfg {Boolean} vertical Orient the Slider vertically rather than horizontally, defaults to false.\r
26086      */\r
26087     vertical: false,\r
26088     /**\r
26089      * @cfg {Number} minValue The minimum value for the Slider. Defaults to 0.\r
26090      */\r
26091     minValue: 0,\r
26092     /**\r
26093      * @cfg {Number} maxValue The maximum value for the Slider. Defaults to 100.\r
26094      */\r
26095     maxValue: 100,\r
26096     /**\r
26097      * @cfg {Number/Boolean} decimalPrecision.\r
26098      * <p>The number of decimal places to which to round the Slider's value. Defaults to 0.</p>\r
26099      * <p>To disable rounding, configure as <tt><b>false</b></tt>.</p>\r
26100      */\r
26101     decimalPrecision: 0,\r
26102     /**\r
26103      * @cfg {Number} keyIncrement How many units to change the Slider when adjusting with keyboard navigation. Defaults to 1. If the increment config is larger, it will be used instead.\r
26104      */\r
26105     keyIncrement: 1,\r
26106     /**\r
26107      * @cfg {Number} increment How many units to change the slider when adjusting by drag and drop. Use this option to enable 'snapping'.\r
26108      */\r
26109     increment: 0,\r
26110     // private\r
26111     clickRange: [5,15],\r
26112     /**\r
26113      * @cfg {Boolean} clickToChange Determines whether or not clicking on the Slider axis will change the slider. Defaults to true\r
26114      */\r
26115     clickToChange : true,\r
26116     /**\r
26117      * @cfg {Boolean} animate Turn on or off animation. Defaults to true\r
26118      */\r
26119     animate: true,\r
26120 \r
26121     /**\r
26122      * True while the thumb is in a drag operation\r
26123      * @type boolean\r
26124      */\r
26125     dragging: false,\r
26126 \r
26127     // private override\r
26128     initComponent : function(){\r
26129         if(!Ext.isDefined(this.value)){\r
26130             this.value = this.minValue;\r
26131         }\r
26132         Ext.Slider.superclass.initComponent.call(this);\r
26133         this.keyIncrement = Math.max(this.increment, this.keyIncrement);\r
26134         this.addEvents(\r
26135             /**\r
26136              * @event beforechange\r
26137              * Fires before the slider value is changed. By returning false from an event handler,\r
26138              * you can cancel the event and prevent the slider from changing.\r
26139              * @param {Ext.Slider} slider The slider\r
26140              * @param {Number} newValue The new value which the slider is being changed to.\r
26141              * @param {Number} oldValue The old value which the slider was previously.\r
26142              */\r
26143             'beforechange',\r
26144             /**\r
26145              * @event change\r
26146              * Fires when the slider value is changed.\r
26147              * @param {Ext.Slider} slider The slider\r
26148              * @param {Number} newValue The new value which the slider has been changed to.\r
26149              */\r
26150             'change',\r
26151             /**\r
26152              * @event changecomplete\r
26153              * Fires when the slider value is changed by the user and any drag operations have completed.\r
26154              * @param {Ext.Slider} slider The slider\r
26155              * @param {Number} newValue The new value which the slider has been changed to.\r
26156              */\r
26157             'changecomplete',\r
26158             /**\r
26159              * @event dragstart\r
26160              * Fires after a drag operation has started.\r
26161              * @param {Ext.Slider} slider The slider\r
26162              * @param {Ext.EventObject} e The event fired from Ext.dd.DragTracker\r
26163              */\r
26164             'dragstart',\r
26165             /**\r
26166              * @event drag\r
26167              * Fires continuously during the drag operation while the mouse is moving.\r
26168              * @param {Ext.Slider} slider The slider\r
26169              * @param {Ext.EventObject} e The event fired from Ext.dd.DragTracker\r
26170              */\r
26171             'drag',\r
26172             /**\r
26173              * @event dragend\r
26174              * Fires after the drag operation has completed.\r
26175              * @param {Ext.Slider} slider The slider\r
26176              * @param {Ext.EventObject} e The event fired from Ext.dd.DragTracker\r
26177              */\r
26178             'dragend'\r
26179         );\r
26180 \r
26181         if(this.vertical){\r
26182             Ext.apply(this, Ext.Slider.Vertical);\r
26183         }\r
26184     },\r
26185 \r
26186     // private override\r
26187     onRender : function(){\r
26188         this.autoEl = {\r
26189             cls: 'x-slider ' + (this.vertical ? 'x-slider-vert' : 'x-slider-horz'),\r
26190             cn:{cls:'x-slider-end',cn:{cls:'x-slider-inner',cn:[{cls:'x-slider-thumb'},{tag:'a', cls:'x-slider-focus', href:"#", tabIndex: '-1', hidefocus:'on'}]}}\r
26191         };\r
26192         Ext.Slider.superclass.onRender.apply(this, arguments);\r
26193         this.endEl = this.el.first();\r
26194         this.innerEl = this.endEl.first();\r
26195         this.thumb = this.innerEl.first();\r
26196         this.halfThumb = (this.vertical ? this.thumb.getHeight() : this.thumb.getWidth())/2;\r
26197         this.focusEl = this.thumb.next();\r
26198         this.initEvents();\r
26199     },\r
26200 \r
26201     // private override\r
26202     initEvents : function(){\r
26203         this.thumb.addClassOnOver('x-slider-thumb-over');\r
26204         this.mon(this.el, {\r
26205             scope: this,\r
26206             mousedown: this.onMouseDown,\r
26207             keydown: this.onKeyDown\r
26208         });\r
26209 \r
26210         this.focusEl.swallowEvent("click", true);\r
26211 \r
26212         this.tracker = new Ext.dd.DragTracker({\r
26213             onBeforeStart: this.onBeforeDragStart.createDelegate(this),\r
26214             onStart: this.onDragStart.createDelegate(this),\r
26215             onDrag: this.onDrag.createDelegate(this),\r
26216             onEnd: this.onDragEnd.createDelegate(this),\r
26217             tolerance: 3,\r
26218             autoStart: 300\r
26219         });\r
26220         this.tracker.initEl(this.thumb);\r
26221     },\r
26222 \r
26223     // private override\r
26224     onMouseDown : function(e){\r
26225         if(this.disabled){\r
26226             return;\r
26227         }\r
26228         if(this.clickToChange && e.target != this.thumb.dom){\r
26229             var local = this.innerEl.translatePoints(e.getXY());\r
26230             this.onClickChange(local);\r
26231         }\r
26232         this.focus();\r
26233     },\r
26234 \r
26235     // private\r
26236     onClickChange : function(local){\r
26237         if(local.top > this.clickRange[0] && local.top < this.clickRange[1]){\r
26238             this.setValue(Ext.util.Format.round(this.reverseValue(local.left), this.decimalPrecision), undefined, true);\r
26239         }\r
26240     },\r
26241 \r
26242     // private\r
26243     onKeyDown : function(e){\r
26244         if(this.disabled){e.preventDefault();return;}\r
26245         var k = e.getKey();\r
26246         switch(k){\r
26247             case e.UP:\r
26248             case e.RIGHT:\r
26249                 e.stopEvent();\r
26250                 if(e.ctrlKey){\r
26251                     this.setValue(this.maxValue, undefined, true);\r
26252                 }else{\r
26253                     this.setValue(this.value+this.keyIncrement, undefined, true);\r
26254                 }\r
26255             break;\r
26256             case e.DOWN:\r
26257             case e.LEFT:\r
26258                 e.stopEvent();\r
26259                 if(e.ctrlKey){\r
26260                     this.setValue(this.minValue, undefined, true);\r
26261                 }else{\r
26262                     this.setValue(this.value-this.keyIncrement, undefined, true);\r
26263                 }\r
26264             break;\r
26265             default:\r
26266                 e.preventDefault();\r
26267         }\r
26268     },\r
26269 \r
26270     // private\r
26271     doSnap : function(value){\r
26272         if(!(this.increment && value)){\r
26273             return value;\r
26274         }\r
26275         var newValue = value,\r
26276             inc = this.increment,\r
26277             m = value % inc;\r
26278         if(m != 0){\r
26279             newValue -= m;\r
26280             if(m * 2 > inc){\r
26281                 newValue += inc;\r
26282             }else if(m * 2 < -inc){\r
26283                 newValue -= inc;\r
26284             }\r
26285         }\r
26286         return newValue.constrain(this.minValue,  this.maxValue);\r
26287     },\r
26288 \r
26289     // private\r
26290     afterRender : function(){\r
26291         Ext.Slider.superclass.afterRender.apply(this, arguments);\r
26292         if(this.value !== undefined){\r
26293             var v = this.normalizeValue(this.value);\r
26294             if(v !== this.value){\r
26295                 delete this.value;\r
26296                 this.setValue(v, false);\r
26297             }else{\r
26298                 this.moveThumb(this.translateValue(v), false);\r
26299             }\r
26300         }\r
26301     },\r
26302 \r
26303     // private\r
26304     getRatio : function(){\r
26305         var w = this.innerEl.getWidth(),\r
26306             v = this.maxValue - this.minValue;\r
26307         return v == 0 ? w : (w/v);\r
26308     },\r
26309 \r
26310     // private\r
26311     normalizeValue : function(v){\r
26312         v = this.doSnap(v);\r
26313         v = Ext.util.Format.round(v, this.decimalPrecision);\r
26314         v = v.constrain(this.minValue, this.maxValue);\r
26315         return v;\r
26316     },\r
26317     \r
26318     /**\r
26319      * Sets the minimum value for the slider instance. If the current value is less than the \r
26320      * minimum value, the current value will be changed.\r
26321      * @param {Number} val The new minimum value\r
26322      */\r
26323     setMinValue : function(val){\r
26324         this.minValue = val;\r
26325         this.syncThumb();\r
26326         if(this.value < val){\r
26327             this.setValue(val);\r
26328         }\r
26329     },\r
26330     \r
26331     /**\r
26332      * Sets the maximum value for the slider instance. If the current value is more than the \r
26333      * maximum value, the current value will be changed.\r
26334      * @param {Number} val The new maximum value\r
26335      */\r
26336     setMaxValue : function(val){\r
26337         this.maxValue = val;\r
26338         this.syncThumb();\r
26339         if(this.value > val){\r
26340             this.setValue(val);\r
26341         }\r
26342     },\r
26343 \r
26344     /**\r
26345      * Programmatically sets the value of the Slider. Ensures that the value is constrained within\r
26346      * the minValue and maxValue.\r
26347      * @param {Number} value The value to set the slider to. (This will be constrained within minValue and maxValue)\r
26348      * @param {Boolean} animate Turn on or off animation, defaults to true\r
26349      */\r
26350     setValue : function(v, animate, changeComplete){\r
26351         v = this.normalizeValue(v);\r
26352         if(v !== this.value && this.fireEvent('beforechange', this, v, this.value) !== false){\r
26353             this.value = v;\r
26354             this.moveThumb(this.translateValue(v), animate !== false);\r
26355             this.fireEvent('change', this, v);\r
26356             if(changeComplete){\r
26357                 this.fireEvent('changecomplete', this, v);\r
26358             }\r
26359         }\r
26360     },\r
26361 \r
26362     // private\r
26363     translateValue : function(v){\r
26364         var ratio = this.getRatio();\r
26365         return (v * ratio) - (this.minValue * ratio) - this.halfThumb;\r
26366     },\r
26367 \r
26368     reverseValue : function(pos){\r
26369         var ratio = this.getRatio();\r
26370         return (pos + (this.minValue * ratio)) / ratio;\r
26371     },\r
26372 \r
26373     // private\r
26374     moveThumb: function(v, animate){\r
26375         if(!animate || this.animate === false){\r
26376             this.thumb.setLeft(v);\r
26377         }else{\r
26378             this.thumb.shift({left: v, stopFx: true, duration:.35});\r
26379         }\r
26380     },\r
26381 \r
26382     // private\r
26383     focus : function(){\r
26384         this.focusEl.focus(10);\r
26385     },\r
26386 \r
26387     // private\r
26388     onBeforeDragStart : function(e){\r
26389         return !this.disabled;\r
26390     },\r
26391 \r
26392     // private\r
26393     onDragStart: function(e){\r
26394         this.thumb.addClass('x-slider-thumb-drag');\r
26395         this.dragging = true;\r
26396         this.dragStartValue = this.value;\r
26397         this.fireEvent('dragstart', this, e);\r
26398     },\r
26399 \r
26400     // private\r
26401     onDrag: function(e){\r
26402         var pos = this.innerEl.translatePoints(this.tracker.getXY());\r
26403         this.setValue(Ext.util.Format.round(this.reverseValue(pos.left), this.decimalPrecision), false);\r
26404         this.fireEvent('drag', this, e);\r
26405     },\r
26406 \r
26407     // private\r
26408     onDragEnd: function(e){\r
26409         this.thumb.removeClass('x-slider-thumb-drag');\r
26410         this.dragging = false;\r
26411         this.fireEvent('dragend', this, e);\r
26412         if(this.dragStartValue != this.value){\r
26413             this.fireEvent('changecomplete', this, this.value);\r
26414         }\r
26415     },\r
26416 \r
26417     // private\r
26418     onResize : function(w, h){\r
26419         this.innerEl.setWidth(w - (this.el.getPadding('l') + this.endEl.getPadding('r')));\r
26420         this.syncThumb();\r
26421         Ext.Slider.superclass.onResize.apply(this, arguments);\r
26422     },\r
26423 \r
26424     //private\r
26425     onDisable: function(){\r
26426         Ext.Slider.superclass.onDisable.call(this);\r
26427         this.thumb.addClass(this.disabledClass);\r
26428         if(Ext.isIE){\r
26429             //IE breaks when using overflow visible and opacity other than 1.\r
26430             //Create a place holder for the thumb and display it.\r
26431             var xy = this.thumb.getXY();\r
26432             this.thumb.hide();\r
26433             this.innerEl.addClass(this.disabledClass).dom.disabled = true;\r
26434             if (!this.thumbHolder){\r
26435                 this.thumbHolder = this.endEl.createChild({cls: 'x-slider-thumb ' + this.disabledClass});\r
26436             }\r
26437             this.thumbHolder.show().setXY(xy);\r
26438         }\r
26439     },\r
26440 \r
26441     //private\r
26442     onEnable: function(){\r
26443         Ext.Slider.superclass.onEnable.call(this);\r
26444         this.thumb.removeClass(this.disabledClass);\r
26445         if(Ext.isIE){\r
26446             this.innerEl.removeClass(this.disabledClass).dom.disabled = false;\r
26447             if(this.thumbHolder){\r
26448                 this.thumbHolder.hide();\r
26449             }\r
26450             this.thumb.show();\r
26451             this.syncThumb();\r
26452         }\r
26453     },\r
26454 \r
26455     /**\r
26456      * Synchronizes the thumb position to the proper proportion of the total component width based\r
26457      * on the current slider {@link #value}.  This will be called automatically when the Slider\r
26458      * is resized by a layout, but if it is rendered auto width, this method can be called from\r
26459      * another resize handler to sync the Slider if necessary.\r
26460      */\r
26461     syncThumb : function(){\r
26462         if(this.rendered){\r
26463             this.moveThumb(this.translateValue(this.value));\r
26464         }\r
26465     },\r
26466 \r
26467     /**\r
26468      * Returns the current value of the slider\r
26469      * @return {Number} The current value of the slider\r
26470      */\r
26471     getValue : function(){\r
26472         return this.value;\r
26473     },\r
26474 \r
26475     // private\r
26476     beforeDestroy : function(){\r
26477         Ext.destroyMembers(this, 'endEl', 'innerEl', 'thumb', 'halfThumb', 'focusEl', 'tracker', 'thumbHolder');\r
26478         Ext.Slider.superclass.beforeDestroy.call(this);\r
26479     }\r
26480 });\r
26481 Ext.reg('slider', Ext.Slider);\r
26482 \r
26483 // private class to support vertical sliders\r
26484 Ext.Slider.Vertical = {\r
26485     onResize : function(w, h){\r
26486         this.innerEl.setHeight(h - (this.el.getPadding('t') + this.endEl.getPadding('b')));\r
26487         this.syncThumb();\r
26488     },\r
26489 \r
26490     getRatio : function(){\r
26491         var h = this.innerEl.getHeight(),\r
26492             v = this.maxValue - this.minValue;\r
26493         return h/v;\r
26494     },\r
26495 \r
26496     moveThumb: function(v, animate){\r
26497         if(!animate || this.animate === false){\r
26498             this.thumb.setBottom(v);\r
26499         }else{\r
26500             this.thumb.shift({bottom: v, stopFx: true, duration:.35});\r
26501         }\r
26502     },\r
26503 \r
26504     onDrag: function(e){\r
26505         var pos = this.innerEl.translatePoints(this.tracker.getXY()),\r
26506             bottom = this.innerEl.getHeight()-pos.top;\r
26507         this.setValue(this.minValue + Ext.util.Format.round(bottom/this.getRatio(), this.decimalPrecision), false);\r
26508         this.fireEvent('drag', this, e);\r
26509     },\r
26510 \r
26511     onClickChange : function(local){\r
26512         if(local.left > this.clickRange[0] && local.left < this.clickRange[1]){\r
26513             var bottom = this.innerEl.getHeight() - local.top;\r
26514             this.setValue(this.minValue + Ext.util.Format.round(bottom/this.getRatio(), this.decimalPrecision), undefined, true);\r
26515         }\r
26516     }\r
26517 };/**\r
26518  * @class Ext.ProgressBar\r
26519  * @extends Ext.BoxComponent\r
26520  * <p>An updateable progress bar component.  The progress bar supports two different modes: manual and automatic.</p>\r
26521  * <p>In manual mode, you are responsible for showing, updating (via {@link #updateProgress}) and clearing the\r
26522  * progress bar as needed from your own code.  This method is most appropriate when you want to show progress\r
26523  * throughout an operation that has predictable points of interest at which you can update the control.</p>\r
26524  * <p>In automatic mode, you simply call {@link #wait} and let the progress bar run indefinitely, only clearing it\r
26525  * once the operation is complete.  You can optionally have the progress bar wait for a specific amount of time\r
26526  * and then clear itself.  Automatic mode is most appropriate for timed operations or asynchronous operations in\r
26527  * which you have no need for indicating intermediate progress.</p>\r
26528  * @cfg {Float} value A floating point value between 0 and 1 (e.g., .5, defaults to 0)\r
26529  * @cfg {String} text The progress bar text (defaults to '')\r
26530  * @cfg {Mixed} textEl The element to render the progress text to (defaults to the progress\r
26531  * bar's internal text element)\r
26532  * @cfg {String} id The progress bar element's id (defaults to an auto-generated id)\r
26533  * @xtype progress\r
26534  */\r
26535 Ext.ProgressBar = Ext.extend(Ext.BoxComponent, {\r
26536    /**\r
26537     * @cfg {String} baseCls\r
26538     * The base CSS class to apply to the progress bar's wrapper element (defaults to 'x-progress')\r
26539     */\r
26540     baseCls : 'x-progress',\r
26541     \r
26542     /**\r
26543     * @cfg {Boolean} animate\r
26544     * True to animate the progress bar during transitions (defaults to false)\r
26545     */\r
26546     animate : false,\r
26547 \r
26548     // private\r
26549     waitTimer : null,\r
26550 \r
26551     // private\r
26552     initComponent : function(){\r
26553         Ext.ProgressBar.superclass.initComponent.call(this);\r
26554         this.addEvents(\r
26555             /**\r
26556              * @event update\r
26557              * Fires after each update interval\r
26558              * @param {Ext.ProgressBar} this\r
26559              * @param {Number} The current progress value\r
26560              * @param {String} The current progress text\r
26561              */\r
26562             "update"\r
26563         );\r
26564     },\r
26565 \r
26566     // private\r
26567     onRender : function(ct, position){\r
26568         var tpl = new Ext.Template(\r
26569             '<div class="{cls}-wrap">',\r
26570                 '<div class="{cls}-inner">',\r
26571                     '<div class="{cls}-bar">',\r
26572                         '<div class="{cls}-text">',\r
26573                             '<div>&#160;</div>',\r
26574                         '</div>',\r
26575                     '</div>',\r
26576                     '<div class="{cls}-text {cls}-text-back">',\r
26577                         '<div>&#160;</div>',\r
26578                     '</div>',\r
26579                 '</div>',\r
26580             '</div>'\r
26581         );\r
26582 \r
26583         this.el = position ? tpl.insertBefore(position, {cls: this.baseCls}, true)\r
26584                 : tpl.append(ct, {cls: this.baseCls}, true);\r
26585                         \r
26586         if(this.id){\r
26587             this.el.dom.id = this.id;\r
26588         }\r
26589         var inner = this.el.dom.firstChild;\r
26590         this.progressBar = Ext.get(inner.firstChild);\r
26591 \r
26592         if(this.textEl){\r
26593             //use an external text el\r
26594             this.textEl = Ext.get(this.textEl);\r
26595             delete this.textTopEl;\r
26596         }else{\r
26597             //setup our internal layered text els\r
26598             this.textTopEl = Ext.get(this.progressBar.dom.firstChild);\r
26599             var textBackEl = Ext.get(inner.childNodes[1]);\r
26600             this.textTopEl.setStyle("z-index", 99).addClass('x-hidden');\r
26601             this.textEl = new Ext.CompositeElement([this.textTopEl.dom.firstChild, textBackEl.dom.firstChild]);\r
26602             this.textEl.setWidth(inner.offsetWidth);\r
26603         }\r
26604         this.progressBar.setHeight(inner.offsetHeight);\r
26605     },\r
26606     \r
26607     // private\r
26608     afterRender : function(){\r
26609         Ext.ProgressBar.superclass.afterRender.call(this);\r
26610         if(this.value){\r
26611             this.updateProgress(this.value, this.text);\r
26612         }else{\r
26613             this.updateText(this.text);\r
26614         }\r
26615     },\r
26616 \r
26617     /**\r
26618      * Updates the progress bar value, and optionally its text.  If the text argument is not specified,\r
26619      * any existing text value will be unchanged.  To blank out existing text, pass ''.  Note that even\r
26620      * if the progress bar value exceeds 1, it will never automatically reset -- you are responsible for\r
26621      * determining when the progress is complete and calling {@link #reset} to clear and/or hide the control.\r
26622      * @param {Float} value (optional) A floating point value between 0 and 1 (e.g., .5, defaults to 0)\r
26623      * @param {String} text (optional) The string to display in the progress text element (defaults to '')\r
26624      * @param {Boolean} animate (optional) Whether to animate the transition of the progress bar. If this value is\r
26625      * not specified, the default for the class is used (default to false)\r
26626      * @return {Ext.ProgressBar} this\r
26627      */\r
26628     updateProgress : function(value, text, animate){\r
26629         this.value = value || 0;\r
26630         if(text){\r
26631             this.updateText(text);\r
26632         }\r
26633         if(this.rendered && !this.isDestroyed){\r
26634             var w = Math.floor(value*this.el.dom.firstChild.offsetWidth);\r
26635             this.progressBar.setWidth(w, animate === true || (animate !== false && this.animate));\r
26636             if(this.textTopEl){\r
26637                 //textTopEl should be the same width as the bar so overflow will clip as the bar moves\r
26638                 this.textTopEl.removeClass('x-hidden').setWidth(w);\r
26639             }\r
26640         }\r
26641         this.fireEvent('update', this, value, text);\r
26642         return this;\r
26643     },\r
26644 \r
26645     /**\r
26646      * Initiates an auto-updating progress bar.  A duration can be specified, in which case the progress\r
26647      * bar will automatically reset after a fixed amount of time and optionally call a callback function\r
26648      * if specified.  If no duration is passed in, then the progress bar will run indefinitely and must\r
26649      * be manually cleared by calling {@link #reset}.  The wait method accepts a config object with\r
26650      * the following properties:\r
26651      * <pre>\r
26652 Property   Type          Description\r
26653 ---------- ------------  ----------------------------------------------------------------------\r
26654 duration   Number        The length of time in milliseconds that the progress bar should\r
26655                          run before resetting itself (defaults to undefined, in which case it\r
26656                          will run indefinitely until reset is called)\r
26657 interval   Number        The length of time in milliseconds between each progress update\r
26658                          (defaults to 1000 ms)\r
26659 animate    Boolean       Whether to animate the transition of the progress bar. If this value is\r
26660                          not specified, the default for the class is used.                                                   \r
26661 increment  Number        The number of progress update segments to display within the progress\r
26662                          bar (defaults to 10).  If the bar reaches the end and is still\r
26663                          updating, it will automatically wrap back to the beginning.\r
26664 text       String        Optional text to display in the progress bar element (defaults to '').\r
26665 fn         Function      A callback function to execute after the progress bar finishes auto-\r
26666                          updating.  The function will be called with no arguments.  This function\r
26667                          will be ignored if duration is not specified since in that case the\r
26668                          progress bar can only be stopped programmatically, so any required function\r
26669                          should be called by the same code after it resets the progress bar.\r
26670 scope      Object        The scope that is passed to the callback function (only applies when\r
26671                          duration and fn are both passed).\r
26672 </pre>\r
26673          *\r
26674          * Example usage:\r
26675          * <pre><code>\r
26676 var p = new Ext.ProgressBar({\r
26677    renderTo: 'my-el'\r
26678 });\r
26679 \r
26680 //Wait for 5 seconds, then update the status el (progress bar will auto-reset)\r
26681 p.wait({\r
26682    interval: 100, //bar will move fast!\r
26683    duration: 5000,\r
26684    increment: 15,\r
26685    text: 'Updating...',\r
26686    scope: this,\r
26687    fn: function(){\r
26688       Ext.fly('status').update('Done!');\r
26689    }\r
26690 });\r
26691 \r
26692 //Or update indefinitely until some async action completes, then reset manually\r
26693 p.wait();\r
26694 myAction.on('complete', function(){\r
26695     p.reset();\r
26696     Ext.fly('status').update('Done!');\r
26697 });\r
26698 </code></pre>\r
26699      * @param {Object} config (optional) Configuration options\r
26700      * @return {Ext.ProgressBar} this\r
26701      */\r
26702     wait : function(o){\r
26703         if(!this.waitTimer){\r
26704             var scope = this;\r
26705             o = o || {};\r
26706             this.updateText(o.text);\r
26707             this.waitTimer = Ext.TaskMgr.start({\r
26708                 run: function(i){\r
26709                     var inc = o.increment || 10;\r
26710                     i -= 1;\r
26711                     this.updateProgress(((((i+inc)%inc)+1)*(100/inc))*0.01, null, o.animate);\r
26712                 },\r
26713                 interval: o.interval || 1000,\r
26714                 duration: o.duration,\r
26715                 onStop: function(){\r
26716                     if(o.fn){\r
26717                         o.fn.apply(o.scope || this);\r
26718                     }\r
26719                     this.reset();\r
26720                 },\r
26721                 scope: scope\r
26722             });\r
26723         }\r
26724         return this;\r
26725     },\r
26726 \r
26727     /**\r
26728      * Returns true if the progress bar is currently in a {@link #wait} operation\r
26729      * @return {Boolean} True if waiting, else false\r
26730      */\r
26731     isWaiting : function(){\r
26732         return this.waitTimer !== null;\r
26733     },\r
26734 \r
26735     /**\r
26736      * Updates the progress bar text.  If specified, textEl will be updated, otherwise the progress\r
26737      * bar itself will display the updated text.\r
26738      * @param {String} text (optional) The string to display in the progress text element (defaults to '')\r
26739      * @return {Ext.ProgressBar} this\r
26740      */\r
26741     updateText : function(text){\r
26742         this.text = text || '&#160;';\r
26743         if(this.rendered){\r
26744             this.textEl.update(this.text);\r
26745         }\r
26746         return this;\r
26747     },\r
26748     \r
26749     /**\r
26750      * Synchronizes the inner bar width to the proper proportion of the total componet width based\r
26751      * on the current progress {@link #value}.  This will be called automatically when the ProgressBar\r
26752      * is resized by a layout, but if it is rendered auto width, this method can be called from\r
26753      * another resize handler to sync the ProgressBar if necessary.\r
26754      */\r
26755     syncProgressBar : function(){\r
26756         if(this.value){\r
26757             this.updateProgress(this.value, this.text);\r
26758         }\r
26759         return this;\r
26760     },\r
26761 \r
26762     /**\r
26763      * Sets the size of the progress bar.\r
26764      * @param {Number} width The new width in pixels\r
26765      * @param {Number} height The new height in pixels\r
26766      * @return {Ext.ProgressBar} this\r
26767      */\r
26768     setSize : function(w, h){\r
26769         Ext.ProgressBar.superclass.setSize.call(this, w, h);\r
26770         if(this.textTopEl){\r
26771             var inner = this.el.dom.firstChild;\r
26772             this.textEl.setSize(inner.offsetWidth, inner.offsetHeight);\r
26773         }\r
26774         this.syncProgressBar();\r
26775         return this;\r
26776     },\r
26777 \r
26778     /**\r
26779      * Resets the progress bar value to 0 and text to empty string.  If hide = true, the progress\r
26780      * bar will also be hidden (using the {@link #hideMode} property internally).\r
26781      * @param {Boolean} hide (optional) True to hide the progress bar (defaults to false)\r
26782      * @return {Ext.ProgressBar} this\r
26783      */\r
26784     reset : function(hide){\r
26785         this.updateProgress(0);\r
26786         if(this.textTopEl){\r
26787             this.textTopEl.addClass('x-hidden');\r
26788         }\r
26789         this.clearTimer();\r
26790         if(hide === true){\r
26791             this.hide();\r
26792         }\r
26793         return this;\r
26794     },\r
26795     \r
26796     // private\r
26797     clearTimer : function(){\r
26798         if(this.waitTimer){\r
26799             this.waitTimer.onStop = null; //prevent recursion\r
26800             Ext.TaskMgr.stop(this.waitTimer);\r
26801             this.waitTimer = null;\r
26802         }\r
26803     },\r
26804     \r
26805     onDestroy: function(){\r
26806         this.clearTimer();\r
26807         if(this.rendered){\r
26808             if(this.textEl.isComposite){\r
26809                 this.textEl.clear();\r
26810             }\r
26811             Ext.destroyMembers(this, 'textEl', 'progressBar', 'textTopEl');\r
26812         }\r
26813         Ext.ProgressBar.superclass.onDestroy.call(this);\r
26814     }\r
26815 });\r
26816 Ext.reg('progress', Ext.ProgressBar);/*
26817  * These classes are derivatives of the similarly named classes in the YUI Library.
26818  * The original license:
26819  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
26820  * Code licensed under the BSD License:
26821  * http://developer.yahoo.net/yui/license.txt
26822  */
26823
26824 (function() {
26825
26826 var Event=Ext.EventManager;
26827 var Dom=Ext.lib.Dom;
26828
26829 /**
26830  * @class Ext.dd.DragDrop
26831  * Defines the interface and base operation of items that that can be
26832  * dragged or can be drop targets.  It was designed to be extended, overriding
26833  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
26834  * Up to three html elements can be associated with a DragDrop instance:
26835  * <ul>
26836  * <li>linked element: the element that is passed into the constructor.
26837  * This is the element which defines the boundaries for interaction with
26838  * other DragDrop objects.</li>
26839  * <li>handle element(s): The drag operation only occurs if the element that
26840  * was clicked matches a handle element.  By default this is the linked
26841  * element, but there are times that you will want only a portion of the
26842  * linked element to initiate the drag operation, and the setHandleElId()
26843  * method provides a way to define this.</li>
26844  * <li>drag element: this represents the element that would be moved along
26845  * with the cursor during a drag operation.  By default, this is the linked
26846  * element itself as in {@link Ext.dd.DD}.  setDragElId() lets you define
26847  * a separate element that would be moved, as in {@link Ext.dd.DDProxy}.
26848  * </li>
26849  * </ul>
26850  * This class should not be instantiated until the onload event to ensure that
26851  * the associated elements are available.
26852  * The following would define a DragDrop obj that would interact with any
26853  * other DragDrop obj in the "group1" group:
26854  * <pre>
26855  *  dd = new Ext.dd.DragDrop("div1", "group1");
26856  * </pre>
26857  * Since none of the event handlers have been implemented, nothing would
26858  * actually happen if you were to run the code above.  Normally you would
26859  * override this class or one of the default implementations, but you can
26860  * also override the methods you want on an instance of the class...
26861  * <pre>
26862  *  dd.onDragDrop = function(e, id) {
26863  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
26864  *  }
26865  * </pre>
26866  * @constructor
26867  * @param {String} id of the element that is linked to this instance
26868  * @param {String} sGroup the group of related DragDrop objects
26869  * @param {object} config an object containing configurable attributes
26870  *                Valid properties for DragDrop:
26871  *                    padding, isTarget, maintainOffset, primaryButtonOnly
26872  */
26873 Ext.dd.DragDrop = function(id, sGroup, config) {
26874     if(id) {
26875         this.init(id, sGroup, config);
26876     }
26877 };
26878
26879 Ext.dd.DragDrop.prototype = {
26880
26881     /**
26882      * Set to false to enable a DragDrop object to fire drag events while dragging
26883      * over its own Element. Defaults to true - DragDrop objects do not by default
26884      * fire drag events to themselves.
26885      * @property ignoreSelf
26886      * @type Boolean
26887      */
26888
26889     /**
26890      * The id of the element associated with this object.  This is what we
26891      * refer to as the "linked element" because the size and position of
26892      * this element is used to determine when the drag and drop objects have
26893      * interacted.
26894      * @property id
26895      * @type String
26896      */
26897     id: null,
26898
26899     /**
26900      * Configuration attributes passed into the constructor
26901      * @property config
26902      * @type object
26903      */
26904     config: null,
26905
26906     /**
26907      * The id of the element that will be dragged.  By default this is same
26908      * as the linked element , but could be changed to another element. Ex:
26909      * Ext.dd.DDProxy
26910      * @property dragElId
26911      * @type String
26912      * @private
26913      */
26914     dragElId: null,
26915
26916     /**
26917      * The ID of the element that initiates the drag operation.  By default
26918      * this is the linked element, but could be changed to be a child of this
26919      * element.  This lets us do things like only starting the drag when the
26920      * header element within the linked html element is clicked.
26921      * @property handleElId
26922      * @type String
26923      * @private
26924      */
26925     handleElId: null,
26926
26927     /**
26928      * An object who's property names identify HTML tags to be considered invalid as drag handles.
26929      * A non-null property value identifies the tag as invalid. Defaults to the 
26930      * following value which prevents drag operations from being initiated by &lt;a> elements:<pre><code>
26931 {
26932     A: "A"
26933 }</code></pre>
26934      * @property invalidHandleTypes
26935      * @type Object
26936      */
26937     invalidHandleTypes: null,
26938
26939     /**
26940      * An object who's property names identify the IDs of elements to be considered invalid as drag handles.
26941      * A non-null property value identifies the ID as invalid. For example, to prevent
26942      * dragging from being initiated on element ID "foo", use:<pre><code>
26943 {
26944     foo: true
26945 }</code></pre>
26946      * @property invalidHandleIds
26947      * @type Object
26948      */
26949     invalidHandleIds: null,
26950
26951     /**
26952      * An Array of CSS class names for elements to be considered in valid as drag handles.
26953      * @property invalidHandleClasses
26954      * @type Array
26955      */
26956     invalidHandleClasses: null,
26957
26958     /**
26959      * The linked element's absolute X position at the time the drag was
26960      * started
26961      * @property startPageX
26962      * @type int
26963      * @private
26964      */
26965     startPageX: 0,
26966
26967     /**
26968      * The linked element's absolute X position at the time the drag was
26969      * started
26970      * @property startPageY
26971      * @type int
26972      * @private
26973      */
26974     startPageY: 0,
26975
26976     /**
26977      * The group defines a logical collection of DragDrop objects that are
26978      * related.  Instances only get events when interacting with other
26979      * DragDrop object in the same group.  This lets us define multiple
26980      * groups using a single DragDrop subclass if we want.
26981      * @property groups
26982      * @type object An object in the format {'group1':true, 'group2':true}
26983      */
26984     groups: null,
26985
26986     /**
26987      * Individual drag/drop instances can be locked.  This will prevent
26988      * onmousedown start drag.
26989      * @property locked
26990      * @type boolean
26991      * @private
26992      */
26993     locked: false,
26994
26995     /**
26996      * Lock this instance
26997      * @method lock
26998      */
26999     lock: function() { this.locked = true; },
27000
27001     /**
27002      * When set to true, other DD objects in cooperating DDGroups do not receive
27003      * notification events when this DD object is dragged over them. Defaults to false.
27004      * @property moveOnly
27005      * @type boolean
27006      */
27007     moveOnly: false,
27008
27009     /**
27010      * Unlock this instace
27011      * @method unlock
27012      */
27013     unlock: function() { this.locked = false; },
27014
27015     /**
27016      * By default, all instances can be a drop target.  This can be disabled by
27017      * setting isTarget to false.
27018      * @property isTarget
27019      * @type boolean
27020      */
27021     isTarget: true,
27022
27023     /**
27024      * The padding configured for this drag and drop object for calculating
27025      * the drop zone intersection with this object.
27026      * @property padding
27027      * @type int[] An array containing the 4 padding values: [top, right, bottom, left]
27028      */
27029     padding: null,
27030
27031     /**
27032      * Cached reference to the linked element
27033      * @property _domRef
27034      * @private
27035      */
27036     _domRef: null,
27037
27038     /**
27039      * Internal typeof flag
27040      * @property __ygDragDrop
27041      * @private
27042      */
27043     __ygDragDrop: true,
27044
27045     /**
27046      * Set to true when horizontal contraints are applied
27047      * @property constrainX
27048      * @type boolean
27049      * @private
27050      */
27051     constrainX: false,
27052
27053     /**
27054      * Set to true when vertical contraints are applied
27055      * @property constrainY
27056      * @type boolean
27057      * @private
27058      */
27059     constrainY: false,
27060
27061     /**
27062      * The left constraint
27063      * @property minX
27064      * @type int
27065      * @private
27066      */
27067     minX: 0,
27068
27069     /**
27070      * The right constraint
27071      * @property maxX
27072      * @type int
27073      * @private
27074      */
27075     maxX: 0,
27076
27077     /**
27078      * The up constraint
27079      * @property minY
27080      * @type int
27081      * @type int
27082      * @private
27083      */
27084     minY: 0,
27085
27086     /**
27087      * The down constraint
27088      * @property maxY
27089      * @type int
27090      * @private
27091      */
27092     maxY: 0,
27093
27094     /**
27095      * Maintain offsets when we resetconstraints.  Set to true when you want
27096      * the position of the element relative to its parent to stay the same
27097      * when the page changes
27098      *
27099      * @property maintainOffset
27100      * @type boolean
27101      */
27102     maintainOffset: false,
27103
27104     /**
27105      * Array of pixel locations the element will snap to if we specified a
27106      * horizontal graduation/interval.  This array is generated automatically
27107      * when you define a tick interval.
27108      * @property xTicks
27109      * @type int[]
27110      */
27111     xTicks: null,
27112
27113     /**
27114      * Array of pixel locations the element will snap to if we specified a
27115      * vertical graduation/interval.  This array is generated automatically
27116      * when you define a tick interval.
27117      * @property yTicks
27118      * @type int[]
27119      */
27120     yTicks: null,
27121
27122     /**
27123      * By default the drag and drop instance will only respond to the primary
27124      * button click (left button for a right-handed mouse).  Set to true to
27125      * allow drag and drop to start with any mouse click that is propogated
27126      * by the browser
27127      * @property primaryButtonOnly
27128      * @type boolean
27129      */
27130     primaryButtonOnly: true,
27131
27132     /**
27133      * The availabe property is false until the linked dom element is accessible.
27134      * @property available
27135      * @type boolean
27136      */
27137     available: false,
27138
27139     /**
27140      * By default, drags can only be initiated if the mousedown occurs in the
27141      * region the linked element is.  This is done in part to work around a
27142      * bug in some browsers that mis-report the mousedown if the previous
27143      * mouseup happened outside of the window.  This property is set to true
27144      * if outer handles are defined.
27145      *
27146      * @property hasOuterHandles
27147      * @type boolean
27148      * @default false
27149      */
27150     hasOuterHandles: false,
27151
27152     /**
27153      * Code that executes immediately before the startDrag event
27154      * @method b4StartDrag
27155      * @private
27156      */
27157     b4StartDrag: function(x, y) { },
27158
27159     /**
27160      * Abstract method called after a drag/drop object is clicked
27161      * and the drag or mousedown time thresholds have beeen met.
27162      * @method startDrag
27163      * @param {int} X click location
27164      * @param {int} Y click location
27165      */
27166     startDrag: function(x, y) { /* override this */ },
27167
27168     /**
27169      * Code that executes immediately before the onDrag event
27170      * @method b4Drag
27171      * @private
27172      */
27173     b4Drag: function(e) { },
27174
27175     /**
27176      * Abstract method called during the onMouseMove event while dragging an
27177      * object.
27178      * @method onDrag
27179      * @param {Event} e the mousemove event
27180      */
27181     onDrag: function(e) { /* override this */ },
27182
27183     /**
27184      * Abstract method called when this element fist begins hovering over
27185      * another DragDrop obj
27186      * @method onDragEnter
27187      * @param {Event} e the mousemove event
27188      * @param {String|DragDrop[]} id In POINT mode, the element
27189      * id this is hovering over.  In INTERSECT mode, an array of one or more
27190      * dragdrop items being hovered over.
27191      */
27192     onDragEnter: function(e, id) { /* override this */ },
27193
27194     /**
27195      * Code that executes immediately before the onDragOver event
27196      * @method b4DragOver
27197      * @private
27198      */
27199     b4DragOver: function(e) { },
27200
27201     /**
27202      * Abstract method called when this element is hovering over another
27203      * DragDrop obj
27204      * @method onDragOver
27205      * @param {Event} e the mousemove event
27206      * @param {String|DragDrop[]} id In POINT mode, the element
27207      * id this is hovering over.  In INTERSECT mode, an array of dd items
27208      * being hovered over.
27209      */
27210     onDragOver: function(e, id) { /* override this */ },
27211
27212     /**
27213      * Code that executes immediately before the onDragOut event
27214      * @method b4DragOut
27215      * @private
27216      */
27217     b4DragOut: function(e) { },
27218
27219     /**
27220      * Abstract method called when we are no longer hovering over an element
27221      * @method onDragOut
27222      * @param {Event} e the mousemove event
27223      * @param {String|DragDrop[]} id In POINT mode, the element
27224      * id this was hovering over.  In INTERSECT mode, an array of dd items
27225      * that the mouse is no longer over.
27226      */
27227     onDragOut: function(e, id) { /* override this */ },
27228
27229     /**
27230      * Code that executes immediately before the onDragDrop event
27231      * @method b4DragDrop
27232      * @private
27233      */
27234     b4DragDrop: function(e) { },
27235
27236     /**
27237      * Abstract method called when this item is dropped on another DragDrop
27238      * obj
27239      * @method onDragDrop
27240      * @param {Event} e the mouseup event
27241      * @param {String|DragDrop[]} id In POINT mode, the element
27242      * id this was dropped on.  In INTERSECT mode, an array of dd items this
27243      * was dropped on.
27244      */
27245     onDragDrop: function(e, id) { /* override this */ },
27246
27247     /**
27248      * Abstract method called when this item is dropped on an area with no
27249      * drop target
27250      * @method onInvalidDrop
27251      * @param {Event} e the mouseup event
27252      */
27253     onInvalidDrop: function(e) { /* override this */ },
27254
27255     /**
27256      * Code that executes immediately before the endDrag event
27257      * @method b4EndDrag
27258      * @private
27259      */
27260     b4EndDrag: function(e) { },
27261
27262     /**
27263      * Fired when we are done dragging the object
27264      * @method endDrag
27265      * @param {Event} e the mouseup event
27266      */
27267     endDrag: function(e) { /* override this */ },
27268
27269     /**
27270      * Code executed immediately before the onMouseDown event
27271      * @method b4MouseDown
27272      * @param {Event} e the mousedown event
27273      * @private
27274      */
27275     b4MouseDown: function(e) {  },
27276
27277     /**
27278      * Event handler that fires when a drag/drop obj gets a mousedown
27279      * @method onMouseDown
27280      * @param {Event} e the mousedown event
27281      */
27282     onMouseDown: function(e) { /* override this */ },
27283
27284     /**
27285      * Event handler that fires when a drag/drop obj gets a mouseup
27286      * @method onMouseUp
27287      * @param {Event} e the mouseup event
27288      */
27289     onMouseUp: function(e) { /* override this */ },
27290
27291     /**
27292      * Override the onAvailable method to do what is needed after the initial
27293      * position was determined.
27294      * @method onAvailable
27295      */
27296     onAvailable: function () {
27297     },
27298
27299     /**
27300      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
27301      * @type Object
27302      */
27303     defaultPadding : {left:0, right:0, top:0, bottom:0},
27304
27305     /**
27306      * Initializes the drag drop object's constraints to restrict movement to a certain element.
27307  *
27308  * Usage:
27309  <pre><code>
27310  var dd = new Ext.dd.DDProxy("dragDiv1", "proxytest",
27311                 { dragElId: "existingProxyDiv" });
27312  dd.startDrag = function(){
27313      this.constrainTo("parent-id");
27314  };
27315  </code></pre>
27316  * Or you can initalize it using the {@link Ext.Element} object:
27317  <pre><code>
27318  Ext.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
27319      startDrag : function(){
27320          this.constrainTo("parent-id");
27321      }
27322  });
27323  </code></pre>
27324      * @param {Mixed} constrainTo The element to constrain to.
27325      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
27326      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
27327      * an object containing the sides to pad. For example: {right:10, bottom:10}
27328      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
27329      */
27330     constrainTo : function(constrainTo, pad, inContent){
27331         if(Ext.isNumber(pad)){
27332             pad = {left: pad, right:pad, top:pad, bottom:pad};
27333         }
27334         pad = pad || this.defaultPadding;
27335         var b = Ext.get(this.getEl()).getBox(),
27336             ce = Ext.get(constrainTo),
27337             s = ce.getScroll(),
27338             c, 
27339             cd = ce.dom;
27340         if(cd == document.body){
27341             c = { x: s.left, y: s.top, width: Ext.lib.Dom.getViewWidth(), height: Ext.lib.Dom.getViewHeight()};
27342         }else{
27343             var xy = ce.getXY();
27344             c = {x : xy[0], y: xy[1], width: cd.clientWidth, height: cd.clientHeight};
27345         }
27346
27347
27348         var topSpace = b.y - c.y,
27349             leftSpace = b.x - c.x;
27350
27351         this.resetConstraints();
27352         this.setXConstraint(leftSpace - (pad.left||0), // left
27353                 c.width - leftSpace - b.width - (pad.right||0), //right
27354                                 this.xTickSize
27355         );
27356         this.setYConstraint(topSpace - (pad.top||0), //top
27357                 c.height - topSpace - b.height - (pad.bottom||0), //bottom
27358                                 this.yTickSize
27359         );
27360     },
27361
27362     /**
27363      * Returns a reference to the linked element
27364      * @method getEl
27365      * @return {HTMLElement} the html element
27366      */
27367     getEl: function() {
27368         if (!this._domRef) {
27369             this._domRef = Ext.getDom(this.id);
27370         }
27371
27372         return this._domRef;
27373     },
27374
27375     /**
27376      * Returns a reference to the actual element to drag.  By default this is
27377      * the same as the html element, but it can be assigned to another
27378      * element. An example of this can be found in Ext.dd.DDProxy
27379      * @method getDragEl
27380      * @return {HTMLElement} the html element
27381      */
27382     getDragEl: function() {
27383         return Ext.getDom(this.dragElId);
27384     },
27385
27386     /**
27387      * Sets up the DragDrop object.  Must be called in the constructor of any
27388      * Ext.dd.DragDrop subclass
27389      * @method init
27390      * @param id the id of the linked element
27391      * @param {String} sGroup the group of related items
27392      * @param {object} config configuration attributes
27393      */
27394     init: function(id, sGroup, config) {
27395         this.initTarget(id, sGroup, config);
27396         Event.on(this.id, "mousedown", this.handleMouseDown, this);
27397         // Event.on(this.id, "selectstart", Event.preventDefault);
27398     },
27399
27400     /**
27401      * Initializes Targeting functionality only... the object does not
27402      * get a mousedown handler.
27403      * @method initTarget
27404      * @param id the id of the linked element
27405      * @param {String} sGroup the group of related items
27406      * @param {object} config configuration attributes
27407      */
27408     initTarget: function(id, sGroup, config) {
27409
27410         // configuration attributes
27411         this.config = config || {};
27412
27413         // create a local reference to the drag and drop manager
27414         this.DDM = Ext.dd.DDM;
27415         // initialize the groups array
27416         this.groups = {};
27417
27418         // assume that we have an element reference instead of an id if the
27419         // parameter is not a string
27420         if (typeof id !== "string") {
27421             id = Ext.id(id);
27422         }
27423
27424         // set the id
27425         this.id = id;
27426
27427         // add to an interaction group
27428         this.addToGroup((sGroup) ? sGroup : "default");
27429
27430         // We don't want to register this as the handle with the manager
27431         // so we just set the id rather than calling the setter.
27432         this.handleElId = id;
27433
27434         // the linked element is the element that gets dragged by default
27435         this.setDragElId(id);
27436
27437         // by default, clicked anchors will not start drag operations.
27438         this.invalidHandleTypes = { A: "A" };
27439         this.invalidHandleIds = {};
27440         this.invalidHandleClasses = [];
27441
27442         this.applyConfig();
27443
27444         this.handleOnAvailable();
27445     },
27446
27447     /**
27448      * Applies the configuration parameters that were passed into the constructor.
27449      * This is supposed to happen at each level through the inheritance chain.  So
27450      * a DDProxy implentation will execute apply config on DDProxy, DD, and
27451      * DragDrop in order to get all of the parameters that are available in
27452      * each object.
27453      * @method applyConfig
27454      */
27455     applyConfig: function() {
27456
27457         // configurable properties:
27458         //    padding, isTarget, maintainOffset, primaryButtonOnly
27459         this.padding           = this.config.padding || [0, 0, 0, 0];
27460         this.isTarget          = (this.config.isTarget !== false);
27461         this.maintainOffset    = (this.config.maintainOffset);
27462         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
27463
27464     },
27465
27466     /**
27467      * Executed when the linked element is available
27468      * @method handleOnAvailable
27469      * @private
27470      */
27471     handleOnAvailable: function() {
27472         this.available = true;
27473         this.resetConstraints();
27474         this.onAvailable();
27475     },
27476
27477      /**
27478      * Configures the padding for the target zone in px.  Effectively expands
27479      * (or reduces) the virtual object size for targeting calculations.
27480      * Supports css-style shorthand; if only one parameter is passed, all sides
27481      * will have that padding, and if only two are passed, the top and bottom
27482      * will have the first param, the left and right the second.
27483      * @method setPadding
27484      * @param {int} iTop    Top pad
27485      * @param {int} iRight  Right pad
27486      * @param {int} iBot    Bot pad
27487      * @param {int} iLeft   Left pad
27488      */
27489     setPadding: function(iTop, iRight, iBot, iLeft) {
27490         // this.padding = [iLeft, iRight, iTop, iBot];
27491         if (!iRight && 0 !== iRight) {
27492             this.padding = [iTop, iTop, iTop, iTop];
27493         } else if (!iBot && 0 !== iBot) {
27494             this.padding = [iTop, iRight, iTop, iRight];
27495         } else {
27496             this.padding = [iTop, iRight, iBot, iLeft];
27497         }
27498     },
27499
27500     /**
27501      * Stores the initial placement of the linked element.
27502      * @method setInitPosition
27503      * @param {int} diffX   the X offset, default 0
27504      * @param {int} diffY   the Y offset, default 0
27505      */
27506     setInitPosition: function(diffX, diffY) {
27507         var el = this.getEl();
27508
27509         if (!this.DDM.verifyEl(el)) {
27510             return;
27511         }
27512
27513         var dx = diffX || 0;
27514         var dy = diffY || 0;
27515
27516         var p = Dom.getXY( el );
27517
27518         this.initPageX = p[0] - dx;
27519         this.initPageY = p[1] - dy;
27520
27521         this.lastPageX = p[0];
27522         this.lastPageY = p[1];
27523
27524
27525         this.setStartPosition(p);
27526     },
27527
27528     /**
27529      * Sets the start position of the element.  This is set when the obj
27530      * is initialized, the reset when a drag is started.
27531      * @method setStartPosition
27532      * @param pos current position (from previous lookup)
27533      * @private
27534      */
27535     setStartPosition: function(pos) {
27536         var p = pos || Dom.getXY( this.getEl() );
27537         this.deltaSetXY = null;
27538
27539         this.startPageX = p[0];
27540         this.startPageY = p[1];
27541     },
27542
27543     /**
27544      * Add this instance to a group of related drag/drop objects.  All
27545      * instances belong to at least one group, and can belong to as many
27546      * groups as needed.
27547      * @method addToGroup
27548      * @param sGroup {string} the name of the group
27549      */
27550     addToGroup: function(sGroup) {
27551         this.groups[sGroup] = true;
27552         this.DDM.regDragDrop(this, sGroup);
27553     },
27554
27555     /**
27556      * Remove's this instance from the supplied interaction group
27557      * @method removeFromGroup
27558      * @param {string}  sGroup  The group to drop
27559      */
27560     removeFromGroup: function(sGroup) {
27561         if (this.groups[sGroup]) {
27562             delete this.groups[sGroup];
27563         }
27564
27565         this.DDM.removeDDFromGroup(this, sGroup);
27566     },
27567
27568     /**
27569      * Allows you to specify that an element other than the linked element
27570      * will be moved with the cursor during a drag
27571      * @method setDragElId
27572      * @param id {string} the id of the element that will be used to initiate the drag
27573      */
27574     setDragElId: function(id) {
27575         this.dragElId = id;
27576     },
27577
27578     /**
27579      * Allows you to specify a child of the linked element that should be
27580      * used to initiate the drag operation.  An example of this would be if
27581      * you have a content div with text and links.  Clicking anywhere in the
27582      * content area would normally start the drag operation.  Use this method
27583      * to specify that an element inside of the content div is the element
27584      * that starts the drag operation.
27585      * @method setHandleElId
27586      * @param id {string} the id of the element that will be used to
27587      * initiate the drag.
27588      */
27589     setHandleElId: function(id) {
27590         if (typeof id !== "string") {
27591             id = Ext.id(id);
27592         }
27593         this.handleElId = id;
27594         this.DDM.regHandle(this.id, id);
27595     },
27596
27597     /**
27598      * Allows you to set an element outside of the linked element as a drag
27599      * handle
27600      * @method setOuterHandleElId
27601      * @param id the id of the element that will be used to initiate the drag
27602      */
27603     setOuterHandleElId: function(id) {
27604         if (typeof id !== "string") {
27605             id = Ext.id(id);
27606         }
27607         Event.on(id, "mousedown",
27608                 this.handleMouseDown, this);
27609         this.setHandleElId(id);
27610
27611         this.hasOuterHandles = true;
27612     },
27613
27614     /**
27615      * Remove all drag and drop hooks for this element
27616      * @method unreg
27617      */
27618     unreg: function() {
27619         Event.un(this.id, "mousedown",
27620                 this.handleMouseDown);
27621         this._domRef = null;
27622         this.DDM._remove(this);
27623     },
27624
27625     destroy : function(){
27626         this.unreg();
27627     },
27628
27629     /**
27630      * Returns true if this instance is locked, or the drag drop mgr is locked
27631      * (meaning that all drag/drop is disabled on the page.)
27632      * @method isLocked
27633      * @return {boolean} true if this obj or all drag/drop is locked, else
27634      * false
27635      */
27636     isLocked: function() {
27637         return (this.DDM.isLocked() || this.locked);
27638     },
27639
27640     /**
27641      * Fired when this object is clicked
27642      * @method handleMouseDown
27643      * @param {Event} e
27644      * @param {Ext.dd.DragDrop} oDD the clicked dd object (this dd obj)
27645      * @private
27646      */
27647     handleMouseDown: function(e, oDD){
27648         if (this.primaryButtonOnly && e.button != 0) {
27649             return;
27650         }
27651
27652         if (this.isLocked()) {
27653             return;
27654         }
27655
27656         this.DDM.refreshCache(this.groups);
27657
27658         var pt = new Ext.lib.Point(Ext.lib.Event.getPageX(e), Ext.lib.Event.getPageY(e));
27659         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
27660         } else {
27661             if (this.clickValidator(e)) {
27662
27663                 // set the initial element position
27664                 this.setStartPosition();
27665
27666
27667                 this.b4MouseDown(e);
27668                 this.onMouseDown(e);
27669
27670                 this.DDM.handleMouseDown(e, this);
27671
27672                 this.DDM.stopEvent(e);
27673             } else {
27674
27675
27676             }
27677         }
27678     },
27679
27680     clickValidator: function(e) {
27681         var target = e.getTarget();
27682         return ( this.isValidHandleChild(target) &&
27683                     (this.id == this.handleElId ||
27684                         this.DDM.handleWasClicked(target, this.id)) );
27685     },
27686
27687     /**
27688      * Allows you to specify a tag name that should not start a drag operation
27689      * when clicked.  This is designed to facilitate embedding links within a
27690      * drag handle that do something other than start the drag.
27691      * @method addInvalidHandleType
27692      * @param {string} tagName the type of element to exclude
27693      */
27694     addInvalidHandleType: function(tagName) {
27695         var type = tagName.toUpperCase();
27696         this.invalidHandleTypes[type] = type;
27697     },
27698
27699     /**
27700      * Lets you to specify an element id for a child of a drag handle
27701      * that should not initiate a drag
27702      * @method addInvalidHandleId
27703      * @param {string} id the element id of the element you wish to ignore
27704      */
27705     addInvalidHandleId: function(id) {
27706         if (typeof id !== "string") {
27707             id = Ext.id(id);
27708         }
27709         this.invalidHandleIds[id] = id;
27710     },
27711
27712     /**
27713      * Lets you specify a css class of elements that will not initiate a drag
27714      * @method addInvalidHandleClass
27715      * @param {string} cssClass the class of the elements you wish to ignore
27716      */
27717     addInvalidHandleClass: function(cssClass) {
27718         this.invalidHandleClasses.push(cssClass);
27719     },
27720
27721     /**
27722      * Unsets an excluded tag name set by addInvalidHandleType
27723      * @method removeInvalidHandleType
27724      * @param {string} tagName the type of element to unexclude
27725      */
27726     removeInvalidHandleType: function(tagName) {
27727         var type = tagName.toUpperCase();
27728         // this.invalidHandleTypes[type] = null;
27729         delete this.invalidHandleTypes[type];
27730     },
27731
27732     /**
27733      * Unsets an invalid handle id
27734      * @method removeInvalidHandleId
27735      * @param {string} id the id of the element to re-enable
27736      */
27737     removeInvalidHandleId: function(id) {
27738         if (typeof id !== "string") {
27739             id = Ext.id(id);
27740         }
27741         delete this.invalidHandleIds[id];
27742     },
27743
27744     /**
27745      * Unsets an invalid css class
27746      * @method removeInvalidHandleClass
27747      * @param {string} cssClass the class of the element(s) you wish to
27748      * re-enable
27749      */
27750     removeInvalidHandleClass: function(cssClass) {
27751         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
27752             if (this.invalidHandleClasses[i] == cssClass) {
27753                 delete this.invalidHandleClasses[i];
27754             }
27755         }
27756     },
27757
27758     /**
27759      * Checks the tag exclusion list to see if this click should be ignored
27760      * @method isValidHandleChild
27761      * @param {HTMLElement} node the HTMLElement to evaluate
27762      * @return {boolean} true if this is a valid tag type, false if not
27763      */
27764     isValidHandleChild: function(node) {
27765
27766         var valid = true;
27767         // var n = (node.nodeName == "#text") ? node.parentNode : node;
27768         var nodeName;
27769         try {
27770             nodeName = node.nodeName.toUpperCase();
27771         } catch(e) {
27772             nodeName = node.nodeName;
27773         }
27774         valid = valid && !this.invalidHandleTypes[nodeName];
27775         valid = valid && !this.invalidHandleIds[node.id];
27776
27777         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
27778             valid = !Ext.fly(node).hasClass(this.invalidHandleClasses[i]);
27779         }
27780
27781
27782         return valid;
27783
27784     },
27785
27786     /**
27787      * Create the array of horizontal tick marks if an interval was specified
27788      * in setXConstraint().
27789      * @method setXTicks
27790      * @private
27791      */
27792     setXTicks: function(iStartX, iTickSize) {
27793         this.xTicks = [];
27794         this.xTickSize = iTickSize;
27795
27796         var tickMap = {};
27797
27798         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
27799             if (!tickMap[i]) {
27800                 this.xTicks[this.xTicks.length] = i;
27801                 tickMap[i] = true;
27802             }
27803         }
27804
27805         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
27806             if (!tickMap[i]) {
27807                 this.xTicks[this.xTicks.length] = i;
27808                 tickMap[i] = true;
27809             }
27810         }
27811
27812         this.xTicks.sort(this.DDM.numericSort) ;
27813     },
27814
27815     /**
27816      * Create the array of vertical tick marks if an interval was specified in
27817      * setYConstraint().
27818      * @method setYTicks
27819      * @private
27820      */
27821     setYTicks: function(iStartY, iTickSize) {
27822         this.yTicks = [];
27823         this.yTickSize = iTickSize;
27824
27825         var tickMap = {};
27826
27827         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
27828             if (!tickMap[i]) {
27829                 this.yTicks[this.yTicks.length] = i;
27830                 tickMap[i] = true;
27831             }
27832         }
27833
27834         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
27835             if (!tickMap[i]) {
27836                 this.yTicks[this.yTicks.length] = i;
27837                 tickMap[i] = true;
27838             }
27839         }
27840
27841         this.yTicks.sort(this.DDM.numericSort) ;
27842     },
27843
27844     /**
27845      * By default, the element can be dragged any place on the screen.  Use
27846      * this method to limit the horizontal travel of the element.  Pass in
27847      * 0,0 for the parameters if you want to lock the drag to the y axis.
27848      * @method setXConstraint
27849      * @param {int} iLeft the number of pixels the element can move to the left
27850      * @param {int} iRight the number of pixels the element can move to the
27851      * right
27852      * @param {int} iTickSize optional parameter for specifying that the
27853      * element
27854      * should move iTickSize pixels at a time.
27855      */
27856     setXConstraint: function(iLeft, iRight, iTickSize) {
27857         this.leftConstraint = iLeft;
27858         this.rightConstraint = iRight;
27859
27860         this.minX = this.initPageX - iLeft;
27861         this.maxX = this.initPageX + iRight;
27862         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
27863
27864         this.constrainX = true;
27865     },
27866
27867     /**
27868      * Clears any constraints applied to this instance.  Also clears ticks
27869      * since they can't exist independent of a constraint at this time.
27870      * @method clearConstraints
27871      */
27872     clearConstraints: function() {
27873         this.constrainX = false;
27874         this.constrainY = false;
27875         this.clearTicks();
27876     },
27877
27878     /**
27879      * Clears any tick interval defined for this instance
27880      * @method clearTicks
27881      */
27882     clearTicks: function() {
27883         this.xTicks = null;
27884         this.yTicks = null;
27885         this.xTickSize = 0;
27886         this.yTickSize = 0;
27887     },
27888
27889     /**
27890      * By default, the element can be dragged any place on the screen.  Set
27891      * this to limit the vertical travel of the element.  Pass in 0,0 for the
27892      * parameters if you want to lock the drag to the x axis.
27893      * @method setYConstraint
27894      * @param {int} iUp the number of pixels the element can move up
27895      * @param {int} iDown the number of pixels the element can move down
27896      * @param {int} iTickSize optional parameter for specifying that the
27897      * element should move iTickSize pixels at a time.
27898      */
27899     setYConstraint: function(iUp, iDown, iTickSize) {
27900         this.topConstraint = iUp;
27901         this.bottomConstraint = iDown;
27902
27903         this.minY = this.initPageY - iUp;
27904         this.maxY = this.initPageY + iDown;
27905         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
27906
27907         this.constrainY = true;
27908
27909     },
27910
27911     /**
27912      * resetConstraints must be called if you manually reposition a dd element.
27913      * @method resetConstraints
27914      * @param {boolean} maintainOffset
27915      */
27916     resetConstraints: function() {
27917
27918
27919         // Maintain offsets if necessary
27920         if (this.initPageX || this.initPageX === 0) {
27921             // figure out how much this thing has moved
27922             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
27923             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
27924
27925             this.setInitPosition(dx, dy);
27926
27927         // This is the first time we have detected the element's position
27928         } else {
27929             this.setInitPosition();
27930         }
27931
27932         if (this.constrainX) {
27933             this.setXConstraint( this.leftConstraint,
27934                                  this.rightConstraint,
27935                                  this.xTickSize        );
27936         }
27937
27938         if (this.constrainY) {
27939             this.setYConstraint( this.topConstraint,
27940                                  this.bottomConstraint,
27941                                  this.yTickSize         );
27942         }
27943     },
27944
27945     /**
27946      * Normally the drag element is moved pixel by pixel, but we can specify
27947      * that it move a number of pixels at a time.  This method resolves the
27948      * location when we have it set up like this.
27949      * @method getTick
27950      * @param {int} val where we want to place the object
27951      * @param {int[]} tickArray sorted array of valid points
27952      * @return {int} the closest tick
27953      * @private
27954      */
27955     getTick: function(val, tickArray) {
27956
27957         if (!tickArray) {
27958             // If tick interval is not defined, it is effectively 1 pixel,
27959             // so we return the value passed to us.
27960             return val;
27961         } else if (tickArray[0] >= val) {
27962             // The value is lower than the first tick, so we return the first
27963             // tick.
27964             return tickArray[0];
27965         } else {
27966             for (var i=0, len=tickArray.length; i<len; ++i) {
27967                 var next = i + 1;
27968                 if (tickArray[next] && tickArray[next] >= val) {
27969                     var diff1 = val - tickArray[i];
27970                     var diff2 = tickArray[next] - val;
27971                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
27972                 }
27973             }
27974
27975             // The value is larger than the last tick, so we return the last
27976             // tick.
27977             return tickArray[tickArray.length - 1];
27978         }
27979     },
27980
27981     /**
27982      * toString method
27983      * @method toString
27984      * @return {string} string representation of the dd obj
27985      */
27986     toString: function() {
27987         return ("DragDrop " + this.id);
27988     }
27989
27990 };
27991
27992 })();
27993 /**
27994  * The drag and drop utility provides a framework for building drag and drop
27995  * applications.  In addition to enabling drag and drop for specific elements,
27996  * the drag and drop elements are tracked by the manager class, and the
27997  * interactions between the various elements are tracked during the drag and
27998  * the implementing code is notified about these important moments.
27999  */
28000
28001 // Only load the library once.  Rewriting the manager class would orphan
28002 // existing drag and drop instances.
28003 if (!Ext.dd.DragDropMgr) {
28004
28005 /**
28006  * @class Ext.dd.DragDropMgr
28007  * DragDropMgr is a singleton that tracks the element interaction for
28008  * all DragDrop items in the window.  Generally, you will not call
28009  * this class directly, but it does have helper methods that could
28010  * be useful in your DragDrop implementations.
28011  * @singleton
28012  */
28013 Ext.dd.DragDropMgr = function() {
28014
28015     var Event = Ext.EventManager;
28016
28017     return {
28018
28019         /**
28020          * Two dimensional Array of registered DragDrop objects.  The first
28021          * dimension is the DragDrop item group, the second the DragDrop
28022          * object.
28023          * @property ids
28024          * @type {string: string}
28025          * @private
28026          * @static
28027          */
28028         ids: {},
28029
28030         /**
28031          * Array of element ids defined as drag handles.  Used to determine
28032          * if the element that generated the mousedown event is actually the
28033          * handle and not the html element itself.
28034          * @property handleIds
28035          * @type {string: string}
28036          * @private
28037          * @static
28038          */
28039         handleIds: {},
28040
28041         /**
28042          * the DragDrop object that is currently being dragged
28043          * @property dragCurrent
28044          * @type DragDrop
28045          * @private
28046          * @static
28047          **/
28048         dragCurrent: null,
28049
28050         /**
28051          * the DragDrop object(s) that are being hovered over
28052          * @property dragOvers
28053          * @type Array
28054          * @private
28055          * @static
28056          */
28057         dragOvers: {},
28058
28059         /**
28060          * the X distance between the cursor and the object being dragged
28061          * @property deltaX
28062          * @type int
28063          * @private
28064          * @static
28065          */
28066         deltaX: 0,
28067
28068         /**
28069          * the Y distance between the cursor and the object being dragged
28070          * @property deltaY
28071          * @type int
28072          * @private
28073          * @static
28074          */
28075         deltaY: 0,
28076
28077         /**
28078          * Flag to determine if we should prevent the default behavior of the
28079          * events we define. By default this is true, but this can be set to
28080          * false if you need the default behavior (not recommended)
28081          * @property preventDefault
28082          * @type boolean
28083          * @static
28084          */
28085         preventDefault: true,
28086
28087         /**
28088          * Flag to determine if we should stop the propagation of the events
28089          * we generate. This is true by default but you may want to set it to
28090          * false if the html element contains other features that require the
28091          * mouse click.
28092          * @property stopPropagation
28093          * @type boolean
28094          * @static
28095          */
28096         stopPropagation: true,
28097
28098         /**
28099          * Internal flag that is set to true when drag and drop has been
28100          * intialized
28101          * @property initialized
28102          * @private
28103          * @static
28104          */
28105         initialized: false,
28106
28107         /**
28108          * All drag and drop can be disabled.
28109          * @property locked
28110          * @private
28111          * @static
28112          */
28113         locked: false,
28114
28115         /**
28116          * Called the first time an element is registered.
28117          * @method init
28118          * @private
28119          * @static
28120          */
28121         init: function() {
28122             this.initialized = true;
28123         },
28124
28125         /**
28126          * In point mode, drag and drop interaction is defined by the
28127          * location of the cursor during the drag/drop
28128          * @property POINT
28129          * @type int
28130          * @static
28131          */
28132         POINT: 0,
28133
28134         /**
28135          * In intersect mode, drag and drop interaction is defined by the
28136          * overlap of two or more drag and drop objects.
28137          * @property INTERSECT
28138          * @type int
28139          * @static
28140          */
28141         INTERSECT: 1,
28142
28143         /**
28144          * The current drag and drop mode.  Default: POINT
28145          * @property mode
28146          * @type int
28147          * @static
28148          */
28149         mode: 0,
28150
28151         /**
28152          * Runs method on all drag and drop objects
28153          * @method _execOnAll
28154          * @private
28155          * @static
28156          */
28157         _execOnAll: function(sMethod, args) {
28158             for (var i in this.ids) {
28159                 for (var j in this.ids[i]) {
28160                     var oDD = this.ids[i][j];
28161                     if (! this.isTypeOfDD(oDD)) {
28162                         continue;
28163                     }
28164                     oDD[sMethod].apply(oDD, args);
28165                 }
28166             }
28167         },
28168
28169         /**
28170          * Drag and drop initialization.  Sets up the global event handlers
28171          * @method _onLoad
28172          * @private
28173          * @static
28174          */
28175         _onLoad: function() {
28176
28177             this.init();
28178
28179
28180             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
28181             Event.on(document, "mousemove", this.handleMouseMove, this, true);
28182             Event.on(window,   "unload",    this._onUnload, this, true);
28183             Event.on(window,   "resize",    this._onResize, this, true);
28184             // Event.on(window,   "mouseout",    this._test);
28185
28186         },
28187
28188         /**
28189          * Reset constraints on all drag and drop objs
28190          * @method _onResize
28191          * @private
28192          * @static
28193          */
28194         _onResize: function(e) {
28195             this._execOnAll("resetConstraints", []);
28196         },
28197
28198         /**
28199          * Lock all drag and drop functionality
28200          * @method lock
28201          * @static
28202          */
28203         lock: function() { this.locked = true; },
28204
28205         /**
28206          * Unlock all drag and drop functionality
28207          * @method unlock
28208          * @static
28209          */
28210         unlock: function() { this.locked = false; },
28211
28212         /**
28213          * Is drag and drop locked?
28214          * @method isLocked
28215          * @return {boolean} True if drag and drop is locked, false otherwise.
28216          * @static
28217          */
28218         isLocked: function() { return this.locked; },
28219
28220         /**
28221          * Location cache that is set for all drag drop objects when a drag is
28222          * initiated, cleared when the drag is finished.
28223          * @property locationCache
28224          * @private
28225          * @static
28226          */
28227         locationCache: {},
28228
28229         /**
28230          * Set useCache to false if you want to force object the lookup of each
28231          * drag and drop linked element constantly during a drag.
28232          * @property useCache
28233          * @type boolean
28234          * @static
28235          */
28236         useCache: true,
28237
28238         /**
28239          * The number of pixels that the mouse needs to move after the
28240          * mousedown before the drag is initiated.  Default=3;
28241          * @property clickPixelThresh
28242          * @type int
28243          * @static
28244          */
28245         clickPixelThresh: 3,
28246
28247         /**
28248          * The number of milliseconds after the mousedown event to initiate the
28249          * drag if we don't get a mouseup event. Default=350
28250          * @property clickTimeThresh
28251          * @type int
28252          * @static
28253          */
28254         clickTimeThresh: 350,
28255
28256         /**
28257          * Flag that indicates that either the drag pixel threshold or the
28258          * mousdown time threshold has been met
28259          * @property dragThreshMet
28260          * @type boolean
28261          * @private
28262          * @static
28263          */
28264         dragThreshMet: false,
28265
28266         /**
28267          * Timeout used for the click time threshold
28268          * @property clickTimeout
28269          * @type Object
28270          * @private
28271          * @static
28272          */
28273         clickTimeout: null,
28274
28275         /**
28276          * The X position of the mousedown event stored for later use when a
28277          * drag threshold is met.
28278          * @property startX
28279          * @type int
28280          * @private
28281          * @static
28282          */
28283         startX: 0,
28284
28285         /**
28286          * The Y position of the mousedown event stored for later use when a
28287          * drag threshold is met.
28288          * @property startY
28289          * @type int
28290          * @private
28291          * @static
28292          */
28293         startY: 0,
28294
28295         /**
28296          * Each DragDrop instance must be registered with the DragDropMgr.
28297          * This is executed in DragDrop.init()
28298          * @method regDragDrop
28299          * @param {DragDrop} oDD the DragDrop object to register
28300          * @param {String} sGroup the name of the group this element belongs to
28301          * @static
28302          */
28303         regDragDrop: function(oDD, sGroup) {
28304             if (!this.initialized) { this.init(); }
28305
28306             if (!this.ids[sGroup]) {
28307                 this.ids[sGroup] = {};
28308             }
28309             this.ids[sGroup][oDD.id] = oDD;
28310         },
28311
28312         /**
28313          * Removes the supplied dd instance from the supplied group. Executed
28314          * by DragDrop.removeFromGroup, so don't call this function directly.
28315          * @method removeDDFromGroup
28316          * @private
28317          * @static
28318          */
28319         removeDDFromGroup: function(oDD, sGroup) {
28320             if (!this.ids[sGroup]) {
28321                 this.ids[sGroup] = {};
28322             }
28323
28324             var obj = this.ids[sGroup];
28325             if (obj && obj[oDD.id]) {
28326                 delete obj[oDD.id];
28327             }
28328         },
28329
28330         /**
28331          * Unregisters a drag and drop item.  This is executed in
28332          * DragDrop.unreg, use that method instead of calling this directly.
28333          * @method _remove
28334          * @private
28335          * @static
28336          */
28337         _remove: function(oDD) {
28338             for (var g in oDD.groups) {
28339                 if (g && this.ids[g] && this.ids[g][oDD.id]) {
28340                     delete this.ids[g][oDD.id];
28341                 }
28342             }
28343             delete this.handleIds[oDD.id];
28344         },
28345
28346         /**
28347          * Each DragDrop handle element must be registered.  This is done
28348          * automatically when executing DragDrop.setHandleElId()
28349          * @method regHandle
28350          * @param {String} sDDId the DragDrop id this element is a handle for
28351          * @param {String} sHandleId the id of the element that is the drag
28352          * handle
28353          * @static
28354          */
28355         regHandle: function(sDDId, sHandleId) {
28356             if (!this.handleIds[sDDId]) {
28357                 this.handleIds[sDDId] = {};
28358             }
28359             this.handleIds[sDDId][sHandleId] = sHandleId;
28360         },
28361
28362         /**
28363          * Utility function to determine if a given element has been
28364          * registered as a drag drop item.
28365          * @method isDragDrop
28366          * @param {String} id the element id to check
28367          * @return {boolean} true if this element is a DragDrop item,
28368          * false otherwise
28369          * @static
28370          */
28371         isDragDrop: function(id) {
28372             return ( this.getDDById(id) ) ? true : false;
28373         },
28374
28375         /**
28376          * Returns the drag and drop instances that are in all groups the
28377          * passed in instance belongs to.
28378          * @method getRelated
28379          * @param {DragDrop} p_oDD the obj to get related data for
28380          * @param {boolean} bTargetsOnly if true, only return targetable objs
28381          * @return {DragDrop[]} the related instances
28382          * @static
28383          */
28384         getRelated: function(p_oDD, bTargetsOnly) {
28385             var oDDs = [];
28386             for (var i in p_oDD.groups) {
28387                 for (var j in this.ids[i]) {
28388                     var dd = this.ids[i][j];
28389                     if (! this.isTypeOfDD(dd)) {
28390                         continue;
28391                     }
28392                     if (!bTargetsOnly || dd.isTarget) {
28393                         oDDs[oDDs.length] = dd;
28394                     }
28395                 }
28396             }
28397
28398             return oDDs;
28399         },
28400
28401         /**
28402          * Returns true if the specified dd target is a legal target for
28403          * the specifice drag obj
28404          * @method isLegalTarget
28405          * @param {DragDrop} the drag obj
28406          * @param {DragDrop} the target
28407          * @return {boolean} true if the target is a legal target for the
28408          * dd obj
28409          * @static
28410          */
28411         isLegalTarget: function (oDD, oTargetDD) {
28412             var targets = this.getRelated(oDD, true);
28413             for (var i=0, len=targets.length;i<len;++i) {
28414                 if (targets[i].id == oTargetDD.id) {
28415                     return true;
28416                 }
28417             }
28418
28419             return false;
28420         },
28421
28422         /**
28423          * My goal is to be able to transparently determine if an object is
28424          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
28425          * returns "object", oDD.constructor.toString() always returns
28426          * "DragDrop" and not the name of the subclass.  So for now it just
28427          * evaluates a well-known variable in DragDrop.
28428          * @method isTypeOfDD
28429          * @param {Object} the object to evaluate
28430          * @return {boolean} true if typeof oDD = DragDrop
28431          * @static
28432          */
28433         isTypeOfDD: function (oDD) {
28434             return (oDD && oDD.__ygDragDrop);
28435         },
28436
28437         /**
28438          * Utility function to determine if a given element has been
28439          * registered as a drag drop handle for the given Drag Drop object.
28440          * @method isHandle
28441          * @param {String} id the element id to check
28442          * @return {boolean} true if this element is a DragDrop handle, false
28443          * otherwise
28444          * @static
28445          */
28446         isHandle: function(sDDId, sHandleId) {
28447             return ( this.handleIds[sDDId] &&
28448                             this.handleIds[sDDId][sHandleId] );
28449         },
28450
28451         /**
28452          * Returns the DragDrop instance for a given id
28453          * @method getDDById
28454          * @param {String} id the id of the DragDrop object
28455          * @return {DragDrop} the drag drop object, null if it is not found
28456          * @static
28457          */
28458         getDDById: function(id) {
28459             for (var i in this.ids) {
28460                 if (this.ids[i][id]) {
28461                     return this.ids[i][id];
28462                 }
28463             }
28464             return null;
28465         },
28466
28467         /**
28468          * Fired after a registered DragDrop object gets the mousedown event.
28469          * Sets up the events required to track the object being dragged
28470          * @method handleMouseDown
28471          * @param {Event} e the event
28472          * @param oDD the DragDrop object being dragged
28473          * @private
28474          * @static
28475          */
28476         handleMouseDown: function(e, oDD) {
28477             if(Ext.QuickTips){
28478                 Ext.QuickTips.disable();
28479             }
28480             if(this.dragCurrent){
28481                 // the original browser mouseup wasn't handled (e.g. outside FF browser window)
28482                 // so clean up first to avoid breaking the next drag
28483                 this.handleMouseUp(e);
28484             }
28485             
28486             this.currentTarget = e.getTarget();
28487             this.dragCurrent = oDD;
28488
28489             var el = oDD.getEl();
28490
28491             // track start position
28492             this.startX = e.getPageX();
28493             this.startY = e.getPageY();
28494
28495             this.deltaX = this.startX - el.offsetLeft;
28496             this.deltaY = this.startY - el.offsetTop;
28497
28498             this.dragThreshMet = false;
28499
28500             this.clickTimeout = setTimeout(
28501                     function() {
28502                         var DDM = Ext.dd.DDM;
28503                         DDM.startDrag(DDM.startX, DDM.startY);
28504                     },
28505                     this.clickTimeThresh );
28506         },
28507
28508         /**
28509          * Fired when either the drag pixel threshol or the mousedown hold
28510          * time threshold has been met.
28511          * @method startDrag
28512          * @param x {int} the X position of the original mousedown
28513          * @param y {int} the Y position of the original mousedown
28514          * @static
28515          */
28516         startDrag: function(x, y) {
28517             clearTimeout(this.clickTimeout);
28518             if (this.dragCurrent) {
28519                 this.dragCurrent.b4StartDrag(x, y);
28520                 this.dragCurrent.startDrag(x, y);
28521             }
28522             this.dragThreshMet = true;
28523         },
28524
28525         /**
28526          * Internal function to handle the mouseup event.  Will be invoked
28527          * from the context of the document.
28528          * @method handleMouseUp
28529          * @param {Event} e the event
28530          * @private
28531          * @static
28532          */
28533         handleMouseUp: function(e) {
28534
28535             if(Ext.QuickTips){
28536                 Ext.QuickTips.enable();
28537             }
28538             if (! this.dragCurrent) {
28539                 return;
28540             }
28541
28542             clearTimeout(this.clickTimeout);
28543
28544             if (this.dragThreshMet) {
28545                 this.fireEvents(e, true);
28546             } else {
28547             }
28548
28549             this.stopDrag(e);
28550
28551             this.stopEvent(e);
28552         },
28553
28554         /**
28555          * Utility to stop event propagation and event default, if these
28556          * features are turned on.
28557          * @method stopEvent
28558          * @param {Event} e the event as returned by this.getEvent()
28559          * @static
28560          */
28561         stopEvent: function(e){
28562             if(this.stopPropagation) {
28563                 e.stopPropagation();
28564             }
28565
28566             if (this.preventDefault) {
28567                 e.preventDefault();
28568             }
28569         },
28570
28571         /**
28572          * Internal function to clean up event handlers after the drag
28573          * operation is complete
28574          * @method stopDrag
28575          * @param {Event} e the event
28576          * @private
28577          * @static
28578          */
28579         stopDrag: function(e) {
28580             // Fire the drag end event for the item that was dragged
28581             if (this.dragCurrent) {
28582                 if (this.dragThreshMet) {
28583                     this.dragCurrent.b4EndDrag(e);
28584                     this.dragCurrent.endDrag(e);
28585                 }
28586
28587                 this.dragCurrent.onMouseUp(e);
28588             }
28589
28590             this.dragCurrent = null;
28591             this.dragOvers = {};
28592         },
28593
28594         /**
28595          * Internal function to handle the mousemove event.  Will be invoked
28596          * from the context of the html element.
28597          *
28598          * @TODO figure out what we can do about mouse events lost when the
28599          * user drags objects beyond the window boundary.  Currently we can
28600          * detect this in internet explorer by verifying that the mouse is
28601          * down during the mousemove event.  Firefox doesn't give us the
28602          * button state on the mousemove event.
28603          * @method handleMouseMove
28604          * @param {Event} e the event
28605          * @private
28606          * @static
28607          */
28608         handleMouseMove: function(e) {
28609             if (! this.dragCurrent) {
28610                 return true;
28611             }
28612             // var button = e.which || e.button;
28613
28614             // check for IE mouseup outside of page boundary
28615             if (Ext.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
28616                 this.stopEvent(e);
28617                 return this.handleMouseUp(e);
28618             }
28619
28620             if (!this.dragThreshMet) {
28621                 var diffX = Math.abs(this.startX - e.getPageX());
28622                 var diffY = Math.abs(this.startY - e.getPageY());
28623                 if (diffX > this.clickPixelThresh ||
28624                             diffY > this.clickPixelThresh) {
28625                     this.startDrag(this.startX, this.startY);
28626                 }
28627             }
28628
28629             if (this.dragThreshMet) {
28630                 this.dragCurrent.b4Drag(e);
28631                 this.dragCurrent.onDrag(e);
28632                 if(!this.dragCurrent.moveOnly){
28633                     this.fireEvents(e, false);
28634                 }
28635             }
28636
28637             this.stopEvent(e);
28638
28639             return true;
28640         },
28641
28642         /**
28643          * Iterates over all of the DragDrop elements to find ones we are
28644          * hovering over or dropping on
28645          * @method fireEvents
28646          * @param {Event} e the event
28647          * @param {boolean} isDrop is this a drop op or a mouseover op?
28648          * @private
28649          * @static
28650          */
28651         fireEvents: function(e, isDrop) {
28652             var dc = this.dragCurrent;
28653
28654             // If the user did the mouse up outside of the window, we could
28655             // get here even though we have ended the drag.
28656             if (!dc || dc.isLocked()) {
28657                 return;
28658             }
28659
28660             var pt = e.getPoint();
28661
28662             // cache the previous dragOver array
28663             var oldOvers = [];
28664
28665             var outEvts   = [];
28666             var overEvts  = [];
28667             var dropEvts  = [];
28668             var enterEvts = [];
28669
28670             // Check to see if the object(s) we were hovering over is no longer
28671             // being hovered over so we can fire the onDragOut event
28672             for (var i in this.dragOvers) {
28673
28674                 var ddo = this.dragOvers[i];
28675
28676                 if (! this.isTypeOfDD(ddo)) {
28677                     continue;
28678                 }
28679
28680                 if (! this.isOverTarget(pt, ddo, this.mode)) {
28681                     outEvts.push( ddo );
28682                 }
28683
28684                 oldOvers[i] = true;
28685                 delete this.dragOvers[i];
28686             }
28687
28688             for (var sGroup in dc.groups) {
28689
28690                 if ("string" != typeof sGroup) {
28691                     continue;
28692                 }
28693
28694                 for (i in this.ids[sGroup]) {
28695                     var oDD = this.ids[sGroup][i];
28696                     if (! this.isTypeOfDD(oDD)) {
28697                         continue;
28698                     }
28699
28700                     if (oDD.isTarget && !oDD.isLocked() && ((oDD != dc) || (dc.ignoreSelf === false))) {
28701                         if (this.isOverTarget(pt, oDD, this.mode)) {
28702                             // look for drop interactions
28703                             if (isDrop) {
28704                                 dropEvts.push( oDD );
28705                             // look for drag enter and drag over interactions
28706                             } else {
28707
28708                                 // initial drag over: dragEnter fires
28709                                 if (!oldOvers[oDD.id]) {
28710                                     enterEvts.push( oDD );
28711                                 // subsequent drag overs: dragOver fires
28712                                 } else {
28713                                     overEvts.push( oDD );
28714                                 }
28715
28716                                 this.dragOvers[oDD.id] = oDD;
28717                             }
28718                         }
28719                     }
28720                 }
28721             }
28722
28723             if (this.mode) {
28724                 if (outEvts.length) {
28725                     dc.b4DragOut(e, outEvts);
28726                     dc.onDragOut(e, outEvts);
28727                 }
28728
28729                 if (enterEvts.length) {
28730                     dc.onDragEnter(e, enterEvts);
28731                 }
28732
28733                 if (overEvts.length) {
28734                     dc.b4DragOver(e, overEvts);
28735                     dc.onDragOver(e, overEvts);
28736                 }
28737
28738                 if (dropEvts.length) {
28739                     dc.b4DragDrop(e, dropEvts);
28740                     dc.onDragDrop(e, dropEvts);
28741                 }
28742
28743             } else {
28744                 // fire dragout events
28745                 var len = 0;
28746                 for (i=0, len=outEvts.length; i<len; ++i) {
28747                     dc.b4DragOut(e, outEvts[i].id);
28748                     dc.onDragOut(e, outEvts[i].id);
28749                 }
28750
28751                 // fire enter events
28752                 for (i=0,len=enterEvts.length; i<len; ++i) {
28753                     // dc.b4DragEnter(e, oDD.id);
28754                     dc.onDragEnter(e, enterEvts[i].id);
28755                 }
28756
28757                 // fire over events
28758                 for (i=0,len=overEvts.length; i<len; ++i) {
28759                     dc.b4DragOver(e, overEvts[i].id);
28760                     dc.onDragOver(e, overEvts[i].id);
28761                 }
28762
28763                 // fire drop events
28764                 for (i=0, len=dropEvts.length; i<len; ++i) {
28765                     dc.b4DragDrop(e, dropEvts[i].id);
28766                     dc.onDragDrop(e, dropEvts[i].id);
28767                 }
28768
28769             }
28770
28771             // notify about a drop that did not find a target
28772             if (isDrop && !dropEvts.length) {
28773                 dc.onInvalidDrop(e);
28774             }
28775
28776         },
28777
28778         /**
28779          * Helper function for getting the best match from the list of drag
28780          * and drop objects returned by the drag and drop events when we are
28781          * in INTERSECT mode.  It returns either the first object that the
28782          * cursor is over, or the object that has the greatest overlap with
28783          * the dragged element.
28784          * @method getBestMatch
28785          * @param  {DragDrop[]} dds The array of drag and drop objects
28786          * targeted
28787          * @return {DragDrop}       The best single match
28788          * @static
28789          */
28790         getBestMatch: function(dds) {
28791             var winner = null;
28792             // Return null if the input is not what we expect
28793             //if (!dds || !dds.length || dds.length == 0) {
28794                // winner = null;
28795             // If there is only one item, it wins
28796             //} else if (dds.length == 1) {
28797
28798             var len = dds.length;
28799
28800             if (len == 1) {
28801                 winner = dds[0];
28802             } else {
28803                 // Loop through the targeted items
28804                 for (var i=0; i<len; ++i) {
28805                     var dd = dds[i];
28806                     // If the cursor is over the object, it wins.  If the
28807                     // cursor is over multiple matches, the first one we come
28808                     // to wins.
28809                     if (dd.cursorIsOver) {
28810                         winner = dd;
28811                         break;
28812                     // Otherwise the object with the most overlap wins
28813                     } else {
28814                         if (!winner ||
28815                             winner.overlap.getArea() < dd.overlap.getArea()) {
28816                             winner = dd;
28817                         }
28818                     }
28819                 }
28820             }
28821
28822             return winner;
28823         },
28824
28825         /**
28826          * Refreshes the cache of the top-left and bottom-right points of the
28827          * drag and drop objects in the specified group(s).  This is in the
28828          * format that is stored in the drag and drop instance, so typical
28829          * usage is:
28830          * <code>
28831          * Ext.dd.DragDropMgr.refreshCache(ddinstance.groups);
28832          * </code>
28833          * Alternatively:
28834          * <code>
28835          * Ext.dd.DragDropMgr.refreshCache({group1:true, group2:true});
28836          * </code>
28837          * @TODO this really should be an indexed array.  Alternatively this
28838          * method could accept both.
28839          * @method refreshCache
28840          * @param {Object} groups an associative array of groups to refresh
28841          * @static
28842          */
28843         refreshCache: function(groups) {
28844             for (var sGroup in groups) {
28845                 if ("string" != typeof sGroup) {
28846                     continue;
28847                 }
28848                 for (var i in this.ids[sGroup]) {
28849                     var oDD = this.ids[sGroup][i];
28850
28851                     if (this.isTypeOfDD(oDD)) {
28852                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
28853                         var loc = this.getLocation(oDD);
28854                         if (loc) {
28855                             this.locationCache[oDD.id] = loc;
28856                         } else {
28857                             delete this.locationCache[oDD.id];
28858                             // this will unregister the drag and drop object if
28859                             // the element is not in a usable state
28860                             // oDD.unreg();
28861                         }
28862                     }
28863                 }
28864             }
28865         },
28866
28867         /**
28868          * This checks to make sure an element exists and is in the DOM.  The
28869          * main purpose is to handle cases where innerHTML is used to remove
28870          * drag and drop objects from the DOM.  IE provides an 'unspecified
28871          * error' when trying to access the offsetParent of such an element
28872          * @method verifyEl
28873          * @param {HTMLElement} el the element to check
28874          * @return {boolean} true if the element looks usable
28875          * @static
28876          */
28877         verifyEl: function(el) {
28878             if (el) {
28879                 var parent;
28880                 if(Ext.isIE){
28881                     try{
28882                         parent = el.offsetParent;
28883                     }catch(e){}
28884                 }else{
28885                     parent = el.offsetParent;
28886                 }
28887                 if (parent) {
28888                     return true;
28889                 }
28890             }
28891
28892             return false;
28893         },
28894
28895         /**
28896          * Returns a Region object containing the drag and drop element's position
28897          * and size, including the padding configured for it
28898          * @method getLocation
28899          * @param {DragDrop} oDD the drag and drop object to get the
28900          *                       location for
28901          * @return {Ext.lib.Region} a Region object representing the total area
28902          *                             the element occupies, including any padding
28903          *                             the instance is configured for.
28904          * @static
28905          */
28906         getLocation: function(oDD) {
28907             if (! this.isTypeOfDD(oDD)) {
28908                 return null;
28909             }
28910
28911             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
28912
28913             try {
28914                 pos= Ext.lib.Dom.getXY(el);
28915             } catch (e) { }
28916
28917             if (!pos) {
28918                 return null;
28919             }
28920
28921             x1 = pos[0];
28922             x2 = x1 + el.offsetWidth;
28923             y1 = pos[1];
28924             y2 = y1 + el.offsetHeight;
28925
28926             t = y1 - oDD.padding[0];
28927             r = x2 + oDD.padding[1];
28928             b = y2 + oDD.padding[2];
28929             l = x1 - oDD.padding[3];
28930
28931             return new Ext.lib.Region( t, r, b, l );
28932         },
28933
28934         /**
28935          * Checks the cursor location to see if it over the target
28936          * @method isOverTarget
28937          * @param {Ext.lib.Point} pt The point to evaluate
28938          * @param {DragDrop} oTarget the DragDrop object we are inspecting
28939          * @return {boolean} true if the mouse is over the target
28940          * @private
28941          * @static
28942          */
28943         isOverTarget: function(pt, oTarget, intersect) {
28944             // use cache if available
28945             var loc = this.locationCache[oTarget.id];
28946             if (!loc || !this.useCache) {
28947                 loc = this.getLocation(oTarget);
28948                 this.locationCache[oTarget.id] = loc;
28949
28950             }
28951
28952             if (!loc) {
28953                 return false;
28954             }
28955
28956             oTarget.cursorIsOver = loc.contains( pt );
28957
28958             // DragDrop is using this as a sanity check for the initial mousedown
28959             // in this case we are done.  In POINT mode, if the drag obj has no
28960             // contraints, we are also done. Otherwise we need to evaluate the
28961             // location of the target as related to the actual location of the
28962             // dragged element.
28963             var dc = this.dragCurrent;
28964             if (!dc || !dc.getTargetCoord ||
28965                     (!intersect && !dc.constrainX && !dc.constrainY)) {
28966                 return oTarget.cursorIsOver;
28967             }
28968
28969             oTarget.overlap = null;
28970
28971             // Get the current location of the drag element, this is the
28972             // location of the mouse event less the delta that represents
28973             // where the original mousedown happened on the element.  We
28974             // need to consider constraints and ticks as well.
28975             var pos = dc.getTargetCoord(pt.x, pt.y);
28976
28977             var el = dc.getDragEl();
28978             var curRegion = new Ext.lib.Region( pos.y,
28979                                                    pos.x + el.offsetWidth,
28980                                                    pos.y + el.offsetHeight,
28981                                                    pos.x );
28982
28983             var overlap = curRegion.intersect(loc);
28984
28985             if (overlap) {
28986                 oTarget.overlap = overlap;
28987                 return (intersect) ? true : oTarget.cursorIsOver;
28988             } else {
28989                 return false;
28990             }
28991         },
28992
28993         /**
28994          * unload event handler
28995          * @method _onUnload
28996          * @private
28997          * @static
28998          */
28999         _onUnload: function(e, me) {
29000             Ext.dd.DragDropMgr.unregAll();
29001         },
29002
29003         /**
29004          * Cleans up the drag and drop events and objects.
29005          * @method unregAll
29006          * @private
29007          * @static
29008          */
29009         unregAll: function() {
29010
29011             if (this.dragCurrent) {
29012                 this.stopDrag();
29013                 this.dragCurrent = null;
29014             }
29015
29016             this._execOnAll("unreg", []);
29017
29018             for (var i in this.elementCache) {
29019                 delete this.elementCache[i];
29020             }
29021
29022             this.elementCache = {};
29023             this.ids = {};
29024         },
29025
29026         /**
29027          * A cache of DOM elements
29028          * @property elementCache
29029          * @private
29030          * @static
29031          */
29032         elementCache: {},
29033
29034         /**
29035          * Get the wrapper for the DOM element specified
29036          * @method getElWrapper
29037          * @param {String} id the id of the element to get
29038          * @return {Ext.dd.DDM.ElementWrapper} the wrapped element
29039          * @private
29040          * @deprecated This wrapper isn't that useful
29041          * @static
29042          */
29043         getElWrapper: function(id) {
29044             var oWrapper = this.elementCache[id];
29045             if (!oWrapper || !oWrapper.el) {
29046                 oWrapper = this.elementCache[id] =
29047                     new this.ElementWrapper(Ext.getDom(id));
29048             }
29049             return oWrapper;
29050         },
29051
29052         /**
29053          * Returns the actual DOM element
29054          * @method getElement
29055          * @param {String} id the id of the elment to get
29056          * @return {Object} The element
29057          * @deprecated use Ext.lib.Ext.getDom instead
29058          * @static
29059          */
29060         getElement: function(id) {
29061             return Ext.getDom(id);
29062         },
29063
29064         /**
29065          * Returns the style property for the DOM element (i.e.,
29066          * document.getElById(id).style)
29067          * @method getCss
29068          * @param {String} id the id of the elment to get
29069          * @return {Object} The style property of the element
29070          * @deprecated use Ext.lib.Dom instead
29071          * @static
29072          */
29073         getCss: function(id) {
29074             var el = Ext.getDom(id);
29075             return (el) ? el.style : null;
29076         },
29077
29078         /**
29079          * Inner class for cached elements
29080          * @class Ext.dd.DragDropMgr.ElementWrapper
29081          * @for DragDropMgr
29082          * @private
29083          * @deprecated
29084          */
29085         ElementWrapper: function(el) {
29086                 /**
29087                  * The element
29088                  * @property el
29089                  */
29090                 this.el = el || null;
29091                 /**
29092                  * The element id
29093                  * @property id
29094                  */
29095                 this.id = this.el && el.id;
29096                 /**
29097                  * A reference to the style property
29098                  * @property css
29099                  */
29100                 this.css = this.el && el.style;
29101             },
29102
29103         /**
29104          * Returns the X position of an html element
29105          * @method getPosX
29106          * @param el the element for which to get the position
29107          * @return {int} the X coordinate
29108          * @for DragDropMgr
29109          * @deprecated use Ext.lib.Dom.getX instead
29110          * @static
29111          */
29112         getPosX: function(el) {
29113             return Ext.lib.Dom.getX(el);
29114         },
29115
29116         /**
29117          * Returns the Y position of an html element
29118          * @method getPosY
29119          * @param el the element for which to get the position
29120          * @return {int} the Y coordinate
29121          * @deprecated use Ext.lib.Dom.getY instead
29122          * @static
29123          */
29124         getPosY: function(el) {
29125             return Ext.lib.Dom.getY(el);
29126         },
29127
29128         /**
29129          * Swap two nodes.  In IE, we use the native method, for others we
29130          * emulate the IE behavior
29131          * @method swapNode
29132          * @param n1 the first node to swap
29133          * @param n2 the other node to swap
29134          * @static
29135          */
29136         swapNode: function(n1, n2) {
29137             if (n1.swapNode) {
29138                 n1.swapNode(n2);
29139             } else {
29140                 var p = n2.parentNode;
29141                 var s = n2.nextSibling;
29142
29143                 if (s == n1) {
29144                     p.insertBefore(n1, n2);
29145                 } else if (n2 == n1.nextSibling) {
29146                     p.insertBefore(n2, n1);
29147                 } else {
29148                     n1.parentNode.replaceChild(n2, n1);
29149                     p.insertBefore(n1, s);
29150                 }
29151             }
29152         },
29153
29154         /**
29155          * Returns the current scroll position
29156          * @method getScroll
29157          * @private
29158          * @static
29159          */
29160         getScroll: function () {
29161             var t, l, dde=document.documentElement, db=document.body;
29162             if (dde && (dde.scrollTop || dde.scrollLeft)) {
29163                 t = dde.scrollTop;
29164                 l = dde.scrollLeft;
29165             } else if (db) {
29166                 t = db.scrollTop;
29167                 l = db.scrollLeft;
29168             } else {
29169
29170             }
29171             return { top: t, left: l };
29172         },
29173
29174         /**
29175          * Returns the specified element style property
29176          * @method getStyle
29177          * @param {HTMLElement} el          the element
29178          * @param {string}      styleProp   the style property
29179          * @return {string} The value of the style property
29180          * @deprecated use Ext.lib.Dom.getStyle
29181          * @static
29182          */
29183         getStyle: function(el, styleProp) {
29184             return Ext.fly(el).getStyle(styleProp);
29185         },
29186
29187         /**
29188          * Gets the scrollTop
29189          * @method getScrollTop
29190          * @return {int} the document's scrollTop
29191          * @static
29192          */
29193         getScrollTop: function () { return this.getScroll().top; },
29194
29195         /**
29196          * Gets the scrollLeft
29197          * @method getScrollLeft
29198          * @return {int} the document's scrollTop
29199          * @static
29200          */
29201         getScrollLeft: function () { return this.getScroll().left; },
29202
29203         /**
29204          * Sets the x/y position of an element to the location of the
29205          * target element.
29206          * @method moveToEl
29207          * @param {HTMLElement} moveEl      The element to move
29208          * @param {HTMLElement} targetEl    The position reference element
29209          * @static
29210          */
29211         moveToEl: function (moveEl, targetEl) {
29212             var aCoord = Ext.lib.Dom.getXY(targetEl);
29213             Ext.lib.Dom.setXY(moveEl, aCoord);
29214         },
29215
29216         /**
29217          * Numeric array sort function
29218          * @method numericSort
29219          * @static
29220          */
29221         numericSort: function(a, b) { return (a - b); },
29222
29223         /**
29224          * Internal counter
29225          * @property _timeoutCount
29226          * @private
29227          * @static
29228          */
29229         _timeoutCount: 0,
29230
29231         /**
29232          * Trying to make the load order less important.  Without this we get
29233          * an error if this file is loaded before the Event Utility.
29234          * @method _addListeners
29235          * @private
29236          * @static
29237          */
29238         _addListeners: function() {
29239             var DDM = Ext.dd.DDM;
29240             if ( Ext.lib.Event && document ) {
29241                 DDM._onLoad();
29242             } else {
29243                 if (DDM._timeoutCount > 2000) {
29244                 } else {
29245                     setTimeout(DDM._addListeners, 10);
29246                     if (document && document.body) {
29247                         DDM._timeoutCount += 1;
29248                     }
29249                 }
29250             }
29251         },
29252
29253         /**
29254          * Recursively searches the immediate parent and all child nodes for
29255          * the handle element in order to determine wheter or not it was
29256          * clicked.
29257          * @method handleWasClicked
29258          * @param node the html element to inspect
29259          * @static
29260          */
29261         handleWasClicked: function(node, id) {
29262             if (this.isHandle(id, node.id)) {
29263                 return true;
29264             } else {
29265                 // check to see if this is a text node child of the one we want
29266                 var p = node.parentNode;
29267
29268                 while (p) {
29269                     if (this.isHandle(id, p.id)) {
29270                         return true;
29271                     } else {
29272                         p = p.parentNode;
29273                     }
29274                 }
29275             }
29276
29277             return false;
29278         }
29279
29280     };
29281
29282 }();
29283
29284 // shorter alias, save a few bytes
29285 Ext.dd.DDM = Ext.dd.DragDropMgr;
29286 Ext.dd.DDM._addListeners();
29287
29288 }
29289
29290 /**
29291  * @class Ext.dd.DD
29292  * A DragDrop implementation where the linked element follows the
29293  * mouse cursor during a drag.
29294  * @extends Ext.dd.DragDrop
29295  * @constructor
29296  * @param {String} id the id of the linked element
29297  * @param {String} sGroup the group of related DragDrop items
29298  * @param {object} config an object containing configurable attributes
29299  *                Valid properties for DD:
29300  *                    scroll
29301  */
29302 Ext.dd.DD = function(id, sGroup, config) {
29303     if (id) {
29304         this.init(id, sGroup, config);
29305     }
29306 };
29307
29308 Ext.extend(Ext.dd.DD, Ext.dd.DragDrop, {
29309
29310     /**
29311      * When set to true, the utility automatically tries to scroll the browser
29312      * window when a drag and drop element is dragged near the viewport boundary.
29313      * Defaults to true.
29314      * @property scroll
29315      * @type boolean
29316      */
29317     scroll: true,
29318
29319     /**
29320      * Sets the pointer offset to the distance between the linked element's top
29321      * left corner and the location the element was clicked
29322      * @method autoOffset
29323      * @param {int} iPageX the X coordinate of the click
29324      * @param {int} iPageY the Y coordinate of the click
29325      */
29326     autoOffset: function(iPageX, iPageY) {
29327         var x = iPageX - this.startPageX;
29328         var y = iPageY - this.startPageY;
29329         this.setDelta(x, y);
29330     },
29331
29332     /**
29333      * Sets the pointer offset.  You can call this directly to force the
29334      * offset to be in a particular location (e.g., pass in 0,0 to set it
29335      * to the center of the object)
29336      * @method setDelta
29337      * @param {int} iDeltaX the distance from the left
29338      * @param {int} iDeltaY the distance from the top
29339      */
29340     setDelta: function(iDeltaX, iDeltaY) {
29341         this.deltaX = iDeltaX;
29342         this.deltaY = iDeltaY;
29343     },
29344
29345     /**
29346      * Sets the drag element to the location of the mousedown or click event,
29347      * maintaining the cursor location relative to the location on the element
29348      * that was clicked.  Override this if you want to place the element in a
29349      * location other than where the cursor is.
29350      * @method setDragElPos
29351      * @param {int} iPageX the X coordinate of the mousedown or drag event
29352      * @param {int} iPageY the Y coordinate of the mousedown or drag event
29353      */
29354     setDragElPos: function(iPageX, iPageY) {
29355         // the first time we do this, we are going to check to make sure
29356         // the element has css positioning
29357
29358         var el = this.getDragEl();
29359         this.alignElWithMouse(el, iPageX, iPageY);
29360     },
29361
29362     /**
29363      * Sets the element to the location of the mousedown or click event,
29364      * maintaining the cursor location relative to the location on the element
29365      * that was clicked.  Override this if you want to place the element in a
29366      * location other than where the cursor is.
29367      * @method alignElWithMouse
29368      * @param {HTMLElement} el the element to move
29369      * @param {int} iPageX the X coordinate of the mousedown or drag event
29370      * @param {int} iPageY the Y coordinate of the mousedown or drag event
29371      */
29372     alignElWithMouse: function(el, iPageX, iPageY) {
29373         var oCoord = this.getTargetCoord(iPageX, iPageY);
29374         var fly = el.dom ? el : Ext.fly(el, '_dd');
29375         if (!this.deltaSetXY) {
29376             var aCoord = [oCoord.x, oCoord.y];
29377             fly.setXY(aCoord);
29378             var newLeft = fly.getLeft(true);
29379             var newTop  = fly.getTop(true);
29380             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
29381         } else {
29382             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
29383         }
29384
29385         this.cachePosition(oCoord.x, oCoord.y);
29386         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
29387         return oCoord;
29388     },
29389
29390     /**
29391      * Saves the most recent position so that we can reset the constraints and
29392      * tick marks on-demand.  We need to know this so that we can calculate the
29393      * number of pixels the element is offset from its original position.
29394      * @method cachePosition
29395      * @param iPageX the current x position (optional, this just makes it so we
29396      * don't have to look it up again)
29397      * @param iPageY the current y position (optional, this just makes it so we
29398      * don't have to look it up again)
29399      */
29400     cachePosition: function(iPageX, iPageY) {
29401         if (iPageX) {
29402             this.lastPageX = iPageX;
29403             this.lastPageY = iPageY;
29404         } else {
29405             var aCoord = Ext.lib.Dom.getXY(this.getEl());
29406             this.lastPageX = aCoord[0];
29407             this.lastPageY = aCoord[1];
29408         }
29409     },
29410
29411     /**
29412      * Auto-scroll the window if the dragged object has been moved beyond the
29413      * visible window boundary.
29414      * @method autoScroll
29415      * @param {int} x the drag element's x position
29416      * @param {int} y the drag element's y position
29417      * @param {int} h the height of the drag element
29418      * @param {int} w the width of the drag element
29419      * @private
29420      */
29421     autoScroll: function(x, y, h, w) {
29422
29423         if (this.scroll) {
29424             // The client height
29425             var clientH = Ext.lib.Dom.getViewHeight();
29426
29427             // The client width
29428             var clientW = Ext.lib.Dom.getViewWidth();
29429
29430             // The amt scrolled down
29431             var st = this.DDM.getScrollTop();
29432
29433             // The amt scrolled right
29434             var sl = this.DDM.getScrollLeft();
29435
29436             // Location of the bottom of the element
29437             var bot = h + y;
29438
29439             // Location of the right of the element
29440             var right = w + x;
29441
29442             // The distance from the cursor to the bottom of the visible area,
29443             // adjusted so that we don't scroll if the cursor is beyond the
29444             // element drag constraints
29445             var toBot = (clientH + st - y - this.deltaY);
29446
29447             // The distance from the cursor to the right of the visible area
29448             var toRight = (clientW + sl - x - this.deltaX);
29449
29450
29451             // How close to the edge the cursor must be before we scroll
29452             // var thresh = (document.all) ? 100 : 40;
29453             var thresh = 40;
29454
29455             // How many pixels to scroll per autoscroll op.  This helps to reduce
29456             // clunky scrolling. IE is more sensitive about this ... it needs this
29457             // value to be higher.
29458             var scrAmt = (document.all) ? 80 : 30;
29459
29460             // Scroll down if we are near the bottom of the visible page and the
29461             // obj extends below the crease
29462             if ( bot > clientH && toBot < thresh ) {
29463                 window.scrollTo(sl, st + scrAmt);
29464             }
29465
29466             // Scroll up if the window is scrolled down and the top of the object
29467             // goes above the top border
29468             if ( y < st && st > 0 && y - st < thresh ) {
29469                 window.scrollTo(sl, st - scrAmt);
29470             }
29471
29472             // Scroll right if the obj is beyond the right border and the cursor is
29473             // near the border.
29474             if ( right > clientW && toRight < thresh ) {
29475                 window.scrollTo(sl + scrAmt, st);
29476             }
29477
29478             // Scroll left if the window has been scrolled to the right and the obj
29479             // extends past the left border
29480             if ( x < sl && sl > 0 && x - sl < thresh ) {
29481                 window.scrollTo(sl - scrAmt, st);
29482             }
29483         }
29484     },
29485
29486     /**
29487      * Finds the location the element should be placed if we want to move
29488      * it to where the mouse location less the click offset would place us.
29489      * @method getTargetCoord
29490      * @param {int} iPageX the X coordinate of the click
29491      * @param {int} iPageY the Y coordinate of the click
29492      * @return an object that contains the coordinates (Object.x and Object.y)
29493      * @private
29494      */
29495     getTargetCoord: function(iPageX, iPageY) {
29496
29497
29498         var x = iPageX - this.deltaX;
29499         var y = iPageY - this.deltaY;
29500
29501         if (this.constrainX) {
29502             if (x < this.minX) { x = this.minX; }
29503             if (x > this.maxX) { x = this.maxX; }
29504         }
29505
29506         if (this.constrainY) {
29507             if (y < this.minY) { y = this.minY; }
29508             if (y > this.maxY) { y = this.maxY; }
29509         }
29510
29511         x = this.getTick(x, this.xTicks);
29512         y = this.getTick(y, this.yTicks);
29513
29514
29515         return {x:x, y:y};
29516     },
29517
29518     /**
29519      * Sets up config options specific to this class. Overrides
29520      * Ext.dd.DragDrop, but all versions of this method through the
29521      * inheritance chain are called
29522      */
29523     applyConfig: function() {
29524         Ext.dd.DD.superclass.applyConfig.call(this);
29525         this.scroll = (this.config.scroll !== false);
29526     },
29527
29528     /**
29529      * Event that fires prior to the onMouseDown event.  Overrides
29530      * Ext.dd.DragDrop.
29531      */
29532     b4MouseDown: function(e) {
29533         // this.resetConstraints();
29534         this.autoOffset(e.getPageX(),
29535                             e.getPageY());
29536     },
29537
29538     /**
29539      * Event that fires prior to the onDrag event.  Overrides
29540      * Ext.dd.DragDrop.
29541      */
29542     b4Drag: function(e) {
29543         this.setDragElPos(e.getPageX(),
29544                             e.getPageY());
29545     },
29546
29547     toString: function() {
29548         return ("DD " + this.id);
29549     }
29550
29551     //////////////////////////////////////////////////////////////////////////
29552     // Debugging ygDragDrop events that can be overridden
29553     //////////////////////////////////////////////////////////////////////////
29554     /*
29555     startDrag: function(x, y) {
29556     },
29557
29558     onDrag: function(e) {
29559     },
29560
29561     onDragEnter: function(e, id) {
29562     },
29563
29564     onDragOver: function(e, id) {
29565     },
29566
29567     onDragOut: function(e, id) {
29568     },
29569
29570     onDragDrop: function(e, id) {
29571     },
29572
29573     endDrag: function(e) {
29574     }
29575
29576     */
29577
29578 });
29579 /**
29580  * @class Ext.dd.DDProxy
29581  * A DragDrop implementation that inserts an empty, bordered div into
29582  * the document that follows the cursor during drag operations.  At the time of
29583  * the click, the frame div is resized to the dimensions of the linked html
29584  * element, and moved to the exact location of the linked element.
29585  *
29586  * References to the "frame" element refer to the single proxy element that
29587  * was created to be dragged in place of all DDProxy elements on the
29588  * page.
29589  *
29590  * @extends Ext.dd.DD
29591  * @constructor
29592  * @param {String} id the id of the linked html element
29593  * @param {String} sGroup the group of related DragDrop objects
29594  * @param {object} config an object containing configurable attributes
29595  *                Valid properties for DDProxy in addition to those in DragDrop:
29596  *                   resizeFrame, centerFrame, dragElId
29597  */
29598 Ext.dd.DDProxy = function(id, sGroup, config) {
29599     if (id) {
29600         this.init(id, sGroup, config);
29601         this.initFrame();
29602     }
29603 };
29604
29605 /**
29606  * The default drag frame div id
29607  * @property Ext.dd.DDProxy.dragElId
29608  * @type String
29609  * @static
29610  */
29611 Ext.dd.DDProxy.dragElId = "ygddfdiv";
29612
29613 Ext.extend(Ext.dd.DDProxy, Ext.dd.DD, {
29614
29615     /**
29616      * By default we resize the drag frame to be the same size as the element
29617      * we want to drag (this is to get the frame effect).  We can turn it off
29618      * if we want a different behavior.
29619      * @property resizeFrame
29620      * @type boolean
29621      */
29622     resizeFrame: true,
29623
29624     /**
29625      * By default the frame is positioned exactly where the drag element is, so
29626      * we use the cursor offset provided by Ext.dd.DD.  Another option that works only if
29627      * you do not have constraints on the obj is to have the drag frame centered
29628      * around the cursor.  Set centerFrame to true for this effect.
29629      * @property centerFrame
29630      * @type boolean
29631      */
29632     centerFrame: false,
29633
29634     /**
29635      * Creates the proxy element if it does not yet exist
29636      * @method createFrame
29637      */
29638     createFrame: function() {
29639         var self = this;
29640         var body = document.body;
29641
29642         if (!body || !body.firstChild) {
29643             setTimeout( function() { self.createFrame(); }, 50 );
29644             return;
29645         }
29646
29647         var div = this.getDragEl();
29648
29649         if (!div) {
29650             div    = document.createElement("div");
29651             div.id = this.dragElId;
29652             var s  = div.style;
29653
29654             s.position   = "absolute";
29655             s.visibility = "hidden";
29656             s.cursor     = "move";
29657             s.border     = "2px solid #aaa";
29658             s.zIndex     = 999;
29659
29660             // appendChild can blow up IE if invoked prior to the window load event
29661             // while rendering a table.  It is possible there are other scenarios
29662             // that would cause this to happen as well.
29663             body.insertBefore(div, body.firstChild);
29664         }
29665     },
29666
29667     /**
29668      * Initialization for the drag frame element.  Must be called in the
29669      * constructor of all subclasses
29670      * @method initFrame
29671      */
29672     initFrame: function() {
29673         this.createFrame();
29674     },
29675
29676     applyConfig: function() {
29677         Ext.dd.DDProxy.superclass.applyConfig.call(this);
29678
29679         this.resizeFrame = (this.config.resizeFrame !== false);
29680         this.centerFrame = (this.config.centerFrame);
29681         this.setDragElId(this.config.dragElId || Ext.dd.DDProxy.dragElId);
29682     },
29683
29684     /**
29685      * Resizes the drag frame to the dimensions of the clicked object, positions
29686      * it over the object, and finally displays it
29687      * @method showFrame
29688      * @param {int} iPageX X click position
29689      * @param {int} iPageY Y click position
29690      * @private
29691      */
29692     showFrame: function(iPageX, iPageY) {
29693         var el = this.getEl();
29694         var dragEl = this.getDragEl();
29695         var s = dragEl.style;
29696
29697         this._resizeProxy();
29698
29699         if (this.centerFrame) {
29700             this.setDelta( Math.round(parseInt(s.width,  10)/2),
29701                            Math.round(parseInt(s.height, 10)/2) );
29702         }
29703
29704         this.setDragElPos(iPageX, iPageY);
29705
29706         Ext.fly(dragEl).show();
29707     },
29708
29709     /**
29710      * The proxy is automatically resized to the dimensions of the linked
29711      * element when a drag is initiated, unless resizeFrame is set to false
29712      * @method _resizeProxy
29713      * @private
29714      */
29715     _resizeProxy: function() {
29716         if (this.resizeFrame) {
29717             var el = this.getEl();
29718             Ext.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
29719         }
29720     },
29721
29722     // overrides Ext.dd.DragDrop
29723     b4MouseDown: function(e) {
29724         var x = e.getPageX();
29725         var y = e.getPageY();
29726         this.autoOffset(x, y);
29727         this.setDragElPos(x, y);
29728     },
29729
29730     // overrides Ext.dd.DragDrop
29731     b4StartDrag: function(x, y) {
29732         // show the drag frame
29733         this.showFrame(x, y);
29734     },
29735
29736     // overrides Ext.dd.DragDrop
29737     b4EndDrag: function(e) {
29738         Ext.fly(this.getDragEl()).hide();
29739     },
29740
29741     // overrides Ext.dd.DragDrop
29742     // By default we try to move the element to the last location of the frame.
29743     // This is so that the default behavior mirrors that of Ext.dd.DD.
29744     endDrag: function(e) {
29745
29746         var lel = this.getEl();
29747         var del = this.getDragEl();
29748
29749         // Show the drag frame briefly so we can get its position
29750         del.style.visibility = "";
29751
29752         this.beforeMove();
29753         // Hide the linked element before the move to get around a Safari
29754         // rendering bug.
29755         lel.style.visibility = "hidden";
29756         Ext.dd.DDM.moveToEl(lel, del);
29757         del.style.visibility = "hidden";
29758         lel.style.visibility = "";
29759
29760         this.afterDrag();
29761     },
29762
29763     beforeMove : function(){
29764
29765     },
29766
29767     afterDrag : function(){
29768
29769     },
29770
29771     toString: function() {
29772         return ("DDProxy " + this.id);
29773     }
29774
29775 });
29776 /**
29777  * @class Ext.dd.DDTarget
29778  * A DragDrop implementation that does not move, but can be a drop
29779  * target.  You would get the same result by simply omitting implementation
29780  * for the event callbacks, but this way we reduce the processing cost of the
29781  * event listener and the callbacks.
29782  * @extends Ext.dd.DragDrop
29783  * @constructor
29784  * @param {String} id the id of the element that is a drop target
29785  * @param {String} sGroup the group of related DragDrop objects
29786  * @param {object} config an object containing configurable attributes
29787  *                 Valid properties for DDTarget in addition to those in
29788  *                 DragDrop:
29789  *                    none
29790  */
29791 Ext.dd.DDTarget = function(id, sGroup, config) {
29792     if (id) {
29793         this.initTarget(id, sGroup, config);
29794     }
29795 };
29796
29797 // Ext.dd.DDTarget.prototype = new Ext.dd.DragDrop();
29798 Ext.extend(Ext.dd.DDTarget, Ext.dd.DragDrop, {
29799     /**
29800      * @hide
29801      * Overridden and disabled. A DDTarget does not support being dragged.
29802      * @method
29803      */
29804     getDragEl: Ext.emptyFn,
29805     /**
29806      * @hide
29807      * Overridden and disabled. A DDTarget does not support being dragged.
29808      * @method
29809      */
29810     isValidHandleChild: Ext.emptyFn,
29811     /**
29812      * @hide
29813      * Overridden and disabled. A DDTarget does not support being dragged.
29814      * @method
29815      */
29816     startDrag: Ext.emptyFn,
29817     /**
29818      * @hide
29819      * Overridden and disabled. A DDTarget does not support being dragged.
29820      * @method
29821      */
29822     endDrag: Ext.emptyFn,
29823     /**
29824      * @hide
29825      * Overridden and disabled. A DDTarget does not support being dragged.
29826      * @method
29827      */
29828     onDrag: Ext.emptyFn,
29829     /**
29830      * @hide
29831      * Overridden and disabled. A DDTarget does not support being dragged.
29832      * @method
29833      */
29834     onDragDrop: Ext.emptyFn,
29835     /**
29836      * @hide
29837      * Overridden and disabled. A DDTarget does not support being dragged.
29838      * @method
29839      */
29840     onDragEnter: Ext.emptyFn,
29841     /**
29842      * @hide
29843      * Overridden and disabled. A DDTarget does not support being dragged.
29844      * @method
29845      */
29846     onDragOut: Ext.emptyFn,
29847     /**
29848      * @hide
29849      * Overridden and disabled. A DDTarget does not support being dragged.
29850      * @method
29851      */
29852     onDragOver: Ext.emptyFn,
29853     /**
29854      * @hide
29855      * Overridden and disabled. A DDTarget does not support being dragged.
29856      * @method
29857      */
29858     onInvalidDrop: Ext.emptyFn,
29859     /**
29860      * @hide
29861      * Overridden and disabled. A DDTarget does not support being dragged.
29862      * @method
29863      */
29864     onMouseDown: Ext.emptyFn,
29865     /**
29866      * @hide
29867      * Overridden and disabled. A DDTarget does not support being dragged.
29868      * @method
29869      */
29870     onMouseUp: Ext.emptyFn,
29871     /**
29872      * @hide
29873      * Overridden and disabled. A DDTarget does not support being dragged.
29874      * @method
29875      */
29876     setXConstraint: Ext.emptyFn,
29877     /**
29878      * @hide
29879      * Overridden and disabled. A DDTarget does not support being dragged.
29880      * @method
29881      */
29882     setYConstraint: Ext.emptyFn,
29883     /**
29884      * @hide
29885      * Overridden and disabled. A DDTarget does not support being dragged.
29886      * @method
29887      */
29888     resetConstraints: Ext.emptyFn,
29889     /**
29890      * @hide
29891      * Overridden and disabled. A DDTarget does not support being dragged.
29892      * @method
29893      */
29894     clearConstraints: Ext.emptyFn,
29895     /**
29896      * @hide
29897      * Overridden and disabled. A DDTarget does not support being dragged.
29898      * @method
29899      */
29900     clearTicks: Ext.emptyFn,
29901     /**
29902      * @hide
29903      * Overridden and disabled. A DDTarget does not support being dragged.
29904      * @method
29905      */
29906     setInitPosition: Ext.emptyFn,
29907     /**
29908      * @hide
29909      * Overridden and disabled. A DDTarget does not support being dragged.
29910      * @method
29911      */
29912     setDragElId: Ext.emptyFn,
29913     /**
29914      * @hide
29915      * Overridden and disabled. A DDTarget does not support being dragged.
29916      * @method
29917      */
29918     setHandleElId: Ext.emptyFn,
29919     /**
29920      * @hide
29921      * Overridden and disabled. A DDTarget does not support being dragged.
29922      * @method
29923      */
29924     setOuterHandleElId: Ext.emptyFn,
29925     /**
29926      * @hide
29927      * Overridden and disabled. A DDTarget does not support being dragged.
29928      * @method
29929      */
29930     addInvalidHandleClass: Ext.emptyFn,
29931     /**
29932      * @hide
29933      * Overridden and disabled. A DDTarget does not support being dragged.
29934      * @method
29935      */
29936     addInvalidHandleId: Ext.emptyFn,
29937     /**
29938      * @hide
29939      * Overridden and disabled. A DDTarget does not support being dragged.
29940      * @method
29941      */
29942     addInvalidHandleType: Ext.emptyFn,
29943     /**
29944      * @hide
29945      * Overridden and disabled. A DDTarget does not support being dragged.
29946      * @method
29947      */
29948     removeInvalidHandleClass: Ext.emptyFn,
29949     /**
29950      * @hide
29951      * Overridden and disabled. A DDTarget does not support being dragged.
29952      * @method
29953      */
29954     removeInvalidHandleId: Ext.emptyFn,
29955     /**
29956      * @hide
29957      * Overridden and disabled. A DDTarget does not support being dragged.
29958      * @method
29959      */
29960     removeInvalidHandleType: Ext.emptyFn,
29961
29962     toString: function() {
29963         return ("DDTarget " + this.id);
29964     }
29965 });/**\r
29966  * @class Ext.dd.DragTracker\r
29967  * @extends Ext.util.Observable\r
29968  */\r
29969 Ext.dd.DragTracker = Ext.extend(Ext.util.Observable,  {\r
29970     /**\r
29971      * @cfg {Boolean} active\r
29972          * Defaults to <tt>false</tt>.\r
29973          */     \r
29974     active: false,\r
29975     /**\r
29976      * @cfg {Number} tolerance\r
29977          * Defaults to <tt>5</tt>.\r
29978          */     \r
29979     tolerance: 5,\r
29980     /**\r
29981      * @cfg {Boolean/Number} autoStart\r
29982          * Defaults to <tt>false</tt>. Specify <tt>true</tt> to defer trigger start by 1000 ms.\r
29983          * Specify a Number for the number of milliseconds to defer trigger start.\r
29984          */     \r
29985     autoStart: false,\r
29986     \r
29987     constructor : function(config){\r
29988         Ext.apply(this, config);\r
29989             this.addEvents(\r
29990                 /**\r
29991                  * @event mousedown\r
29992                  * @param {Object} this\r
29993                  * @param {Object} e event object\r
29994                  */\r
29995                 'mousedown',\r
29996                 /**\r
29997                  * @event mouseup\r
29998                  * @param {Object} this\r
29999                  * @param {Object} e event object\r
30000                  */\r
30001                 'mouseup',\r
30002                 /**\r
30003                  * @event mousemove\r
30004                  * @param {Object} this\r
30005                  * @param {Object} e event object\r
30006                  */\r
30007                 'mousemove',\r
30008                 /**\r
30009                  * @event dragstart\r
30010                  * @param {Object} this\r
30011                  * @param {Object} startXY the page coordinates of the event\r
30012                  */\r
30013                 'dragstart',\r
30014                 /**\r
30015                  * @event dragend\r
30016                  * @param {Object} this\r
30017                  * @param {Object} e event object\r
30018                  */\r
30019                 'dragend',\r
30020                 /**\r
30021                  * @event drag\r
30022                  * @param {Object} this\r
30023                  * @param {Object} e event object\r
30024                  */\r
30025                 'drag'\r
30026             );\r
30027         \r
30028             this.dragRegion = new Ext.lib.Region(0,0,0,0);\r
30029         \r
30030             if(this.el){\r
30031                 this.initEl(this.el);\r
30032             }\r
30033         Ext.dd.DragTracker.superclass.constructor.call(this, config);\r
30034     },\r
30035 \r
30036     initEl: function(el){\r
30037         this.el = Ext.get(el);\r
30038         el.on('mousedown', this.onMouseDown, this,\r
30039                 this.delegate ? {delegate: this.delegate} : undefined);\r
30040     },\r
30041 \r
30042     destroy : function(){\r
30043         this.el.un('mousedown', this.onMouseDown, this);\r
30044     },\r
30045 \r
30046     onMouseDown: function(e, target){\r
30047         if(this.fireEvent('mousedown', this, e) !== false && this.onBeforeStart(e) !== false){\r
30048             this.startXY = this.lastXY = e.getXY();\r
30049             this.dragTarget = this.delegate ? target : this.el.dom;\r
30050             if(this.preventDefault !== false){\r
30051                 e.preventDefault();\r
30052             }\r
30053             var doc = Ext.getDoc();\r
30054             doc.on('mouseup', this.onMouseUp, this);\r
30055             doc.on('mousemove', this.onMouseMove, this);\r
30056             doc.on('selectstart', this.stopSelect, this);\r
30057             if(this.autoStart){\r
30058                 this.timer = this.triggerStart.defer(this.autoStart === true ? 1000 : this.autoStart, this);\r
30059             }\r
30060         }\r
30061     },\r
30062 \r
30063     onMouseMove: function(e, target){\r
30064         // HACK: IE hack to see if button was released outside of window. */\r
30065         if(this.active && Ext.isIE && !e.browserEvent.button){\r
30066             e.preventDefault();\r
30067             this.onMouseUp(e);\r
30068             return;\r
30069         }\r
30070 \r
30071         e.preventDefault();\r
30072         var xy = e.getXY(), s = this.startXY;\r
30073         this.lastXY = xy;\r
30074         if(!this.active){\r
30075             if(Math.abs(s[0]-xy[0]) > this.tolerance || Math.abs(s[1]-xy[1]) > this.tolerance){\r
30076                 this.triggerStart();\r
30077             }else{\r
30078                 return;\r
30079             }\r
30080         }\r
30081         this.fireEvent('mousemove', this, e);\r
30082         this.onDrag(e);\r
30083         this.fireEvent('drag', this, e);\r
30084     },\r
30085 \r
30086     onMouseUp: function(e){\r
30087         var doc = Ext.getDoc();\r
30088         doc.un('mousemove', this.onMouseMove, this);\r
30089         doc.un('mouseup', this.onMouseUp, this);\r
30090         doc.un('selectstart', this.stopSelect, this);\r
30091         e.preventDefault();\r
30092         this.clearStart();\r
30093         var wasActive = this.active;\r
30094         this.active = false;\r
30095         delete this.elRegion;\r
30096         this.fireEvent('mouseup', this, e);\r
30097         if(wasActive){\r
30098             this.onEnd(e);\r
30099             this.fireEvent('dragend', this, e);\r
30100         }\r
30101     },\r
30102 \r
30103     triggerStart: function(isTimer){\r
30104         this.clearStart();\r
30105         this.active = true;\r
30106         this.onStart(this.startXY);\r
30107         this.fireEvent('dragstart', this, this.startXY);\r
30108     },\r
30109 \r
30110     clearStart : function(){\r
30111         if(this.timer){\r
30112             clearTimeout(this.timer);\r
30113             delete this.timer;\r
30114         }\r
30115     },\r
30116 \r
30117     stopSelect : function(e){\r
30118         e.stopEvent();\r
30119         return false;\r
30120     },\r
30121 \r
30122     onBeforeStart : function(e){\r
30123 \r
30124     },\r
30125 \r
30126     onStart : function(xy){\r
30127 \r
30128     },\r
30129 \r
30130     onDrag : function(e){\r
30131 \r
30132     },\r
30133 \r
30134     onEnd : function(e){\r
30135 \r
30136     },\r
30137 \r
30138     getDragTarget : function(){\r
30139         return this.dragTarget;\r
30140     },\r
30141 \r
30142     getDragCt : function(){\r
30143         return this.el;\r
30144     },\r
30145 \r
30146     getXY : function(constrain){\r
30147         return constrain ?\r
30148                this.constrainModes[constrain].call(this, this.lastXY) : this.lastXY;\r
30149     },\r
30150 \r
30151     getOffset : function(constrain){\r
30152         var xy = this.getXY(constrain);\r
30153         var s = this.startXY;\r
30154         return [s[0]-xy[0], s[1]-xy[1]];\r
30155     },\r
30156 \r
30157     constrainModes: {\r
30158         'point' : function(xy){\r
30159 \r
30160             if(!this.elRegion){\r
30161                 this.elRegion = this.getDragCt().getRegion();\r
30162             }\r
30163 \r
30164             var dr = this.dragRegion;\r
30165 \r
30166             dr.left = xy[0];\r
30167             dr.top = xy[1];\r
30168             dr.right = xy[0];\r
30169             dr.bottom = xy[1];\r
30170 \r
30171             dr.constrainTo(this.elRegion);\r
30172 \r
30173             return [dr.left, dr.top];\r
30174         }\r
30175     }\r
30176 });/**\r
30177  * @class Ext.dd.ScrollManager\r
30178  * <p>Provides automatic scrolling of overflow regions in the page during drag operations.</p>\r
30179  * <p>The ScrollManager configs will be used as the defaults for any scroll container registered with it,\r
30180  * but you can also override most of the configs per scroll container by adding a \r
30181  * <tt>ddScrollConfig</tt> object to the target element that contains these properties: {@link #hthresh},\r
30182  * {@link #vthresh}, {@link #increment} and {@link #frequency}.  Example usage:\r
30183  * <pre><code>\r
30184 var el = Ext.get('scroll-ct');\r
30185 el.ddScrollConfig = {\r
30186     vthresh: 50,\r
30187     hthresh: -1,\r
30188     frequency: 100,\r
30189     increment: 200\r
30190 };\r
30191 Ext.dd.ScrollManager.register(el);\r
30192 </code></pre>\r
30193  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>\r
30194  * @singleton\r
30195  */\r
30196 Ext.dd.ScrollManager = function(){\r
30197     var ddm = Ext.dd.DragDropMgr;\r
30198     var els = {};\r
30199     var dragEl = null;\r
30200     var proc = {};\r
30201     \r
30202     var onStop = function(e){\r
30203         dragEl = null;\r
30204         clearProc();\r
30205     };\r
30206     \r
30207     var triggerRefresh = function(){\r
30208         if(ddm.dragCurrent){\r
30209              ddm.refreshCache(ddm.dragCurrent.groups);\r
30210         }\r
30211     };\r
30212     \r
30213     var doScroll = function(){\r
30214         if(ddm.dragCurrent){\r
30215             var dds = Ext.dd.ScrollManager;\r
30216             var inc = proc.el.ddScrollConfig ?\r
30217                       proc.el.ddScrollConfig.increment : dds.increment;\r
30218             if(!dds.animate){\r
30219                 if(proc.el.scroll(proc.dir, inc)){\r
30220                     triggerRefresh();\r
30221                 }\r
30222             }else{\r
30223                 proc.el.scroll(proc.dir, inc, true, dds.animDuration, triggerRefresh);\r
30224             }\r
30225         }\r
30226     };\r
30227     \r
30228     var clearProc = function(){\r
30229         if(proc.id){\r
30230             clearInterval(proc.id);\r
30231         }\r
30232         proc.id = 0;\r
30233         proc.el = null;\r
30234         proc.dir = "";\r
30235     };\r
30236     \r
30237     var startProc = function(el, dir){\r
30238         clearProc();\r
30239         proc.el = el;\r
30240         proc.dir = dir;\r
30241         var freq = (el.ddScrollConfig && el.ddScrollConfig.frequency) ? \r
30242                 el.ddScrollConfig.frequency : Ext.dd.ScrollManager.frequency;\r
30243         proc.id = setInterval(doScroll, freq);\r
30244     };\r
30245     \r
30246     var onFire = function(e, isDrop){\r
30247         if(isDrop || !ddm.dragCurrent){ return; }\r
30248         var dds = Ext.dd.ScrollManager;\r
30249         if(!dragEl || dragEl != ddm.dragCurrent){\r
30250             dragEl = ddm.dragCurrent;\r
30251             // refresh regions on drag start\r
30252             dds.refreshCache();\r
30253         }\r
30254         \r
30255         var xy = Ext.lib.Event.getXY(e);\r
30256         var pt = new Ext.lib.Point(xy[0], xy[1]);\r
30257         for(var id in els){\r
30258             var el = els[id], r = el._region;\r
30259             var c = el.ddScrollConfig ? el.ddScrollConfig : dds;\r
30260             if(r && r.contains(pt) && el.isScrollable()){\r
30261                 if(r.bottom - pt.y <= c.vthresh){\r
30262                     if(proc.el != el){\r
30263                         startProc(el, "down");\r
30264                     }\r
30265                     return;\r
30266                 }else if(r.right - pt.x <= c.hthresh){\r
30267                     if(proc.el != el){\r
30268                         startProc(el, "left");\r
30269                     }\r
30270                     return;\r
30271                 }else if(pt.y - r.top <= c.vthresh){\r
30272                     if(proc.el != el){\r
30273                         startProc(el, "up");\r
30274                     }\r
30275                     return;\r
30276                 }else if(pt.x - r.left <= c.hthresh){\r
30277                     if(proc.el != el){\r
30278                         startProc(el, "right");\r
30279                     }\r
30280                     return;\r
30281                 }\r
30282             }\r
30283         }\r
30284         clearProc();\r
30285     };\r
30286     \r
30287     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);\r
30288     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);\r
30289     \r
30290     return {\r
30291         /**\r
30292          * Registers new overflow element(s) to auto scroll\r
30293          * @param {Mixed/Array} el The id of or the element to be scrolled or an array of either\r
30294          */\r
30295         register : function(el){\r
30296             if(Ext.isArray(el)){\r
30297                 for(var i = 0, len = el.length; i < len; i++) {\r
30298                         this.register(el[i]);\r
30299                 }\r
30300             }else{\r
30301                 el = Ext.get(el);\r
30302                 els[el.id] = el;\r
30303             }\r
30304         },\r
30305         \r
30306         /**\r
30307          * Unregisters overflow element(s) so they are no longer scrolled\r
30308          * @param {Mixed/Array} el The id of or the element to be removed or an array of either\r
30309          */\r
30310         unregister : function(el){\r
30311             if(Ext.isArray(el)){\r
30312                 for(var i = 0, len = el.length; i < len; i++) {\r
30313                         this.unregister(el[i]);\r
30314                 }\r
30315             }else{\r
30316                 el = Ext.get(el);\r
30317                 delete els[el.id];\r
30318             }\r
30319         },\r
30320         \r
30321         /**\r
30322          * The number of pixels from the top or bottom edge of a container the pointer needs to be to\r
30323          * trigger scrolling (defaults to 25)\r
30324          * @type Number\r
30325          */\r
30326         vthresh : 25,\r
30327         /**\r
30328          * The number of pixels from the right or left edge of a container the pointer needs to be to\r
30329          * trigger scrolling (defaults to 25)\r
30330          * @type Number\r
30331          */\r
30332         hthresh : 25,\r
30333 \r
30334         /**\r
30335          * The number of pixels to scroll in each scroll increment (defaults to 50)\r
30336          * @type Number\r
30337          */\r
30338         increment : 100,\r
30339         \r
30340         /**\r
30341          * The frequency of scrolls in milliseconds (defaults to 500)\r
30342          * @type Number\r
30343          */\r
30344         frequency : 500,\r
30345         \r
30346         /**\r
30347          * True to animate the scroll (defaults to true)\r
30348          * @type Boolean\r
30349          */\r
30350         animate: true,\r
30351         \r
30352         /**\r
30353          * The animation duration in seconds - \r
30354          * MUST BE less than Ext.dd.ScrollManager.frequency! (defaults to .4)\r
30355          * @type Number\r
30356          */\r
30357         animDuration: .4,\r
30358         \r
30359         /**\r
30360          * Manually trigger a cache refresh.\r
30361          */\r
30362         refreshCache : function(){\r
30363             for(var id in els){\r
30364                 if(typeof els[id] == 'object'){ // for people extending the object prototype\r
30365                     els[id]._region = els[id].getRegion();\r
30366                 }\r
30367             }\r
30368         }\r
30369     };\r
30370 }();/**\r
30371  * @class Ext.dd.Registry\r
30372  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either\r
30373  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.\r
30374  * @singleton\r
30375  */\r
30376 Ext.dd.Registry = function(){\r
30377     var elements = {}; \r
30378     var handles = {}; \r
30379     var autoIdSeed = 0;\r
30380 \r
30381     var getId = function(el, autogen){\r
30382         if(typeof el == "string"){\r
30383             return el;\r
30384         }\r
30385         var id = el.id;\r
30386         if(!id && autogen !== false){\r
30387             id = "extdd-" + (++autoIdSeed);\r
30388             el.id = id;\r
30389         }\r
30390         return id;\r
30391     };\r
30392     \r
30393     return {\r
30394     /**\r
30395      * Resgister a drag drop element\r
30396      * @param {String/HTMLElement) element The id or DOM node to register\r
30397      * @param {Object} data (optional) An custom data object that will be passed between the elements that are involved\r
30398      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code\r
30399      * knows how to interpret, plus there are some specific properties known to the Registry that should be\r
30400      * populated in the data object (if applicable):\r
30401      * <pre>\r
30402 Value      Description<br />\r
30403 ---------  ------------------------------------------<br />\r
30404 handles    Array of DOM nodes that trigger dragging<br />\r
30405            for the element being registered<br />\r
30406 isHandle   True if the element passed in triggers<br />\r
30407            dragging itself, else false\r
30408 </pre>\r
30409      */\r
30410         register : function(el, data){\r
30411             data = data || {};\r
30412             if(typeof el == "string"){\r
30413                 el = document.getElementById(el);\r
30414             }\r
30415             data.ddel = el;\r
30416             elements[getId(el)] = data;\r
30417             if(data.isHandle !== false){\r
30418                 handles[data.ddel.id] = data;\r
30419             }\r
30420             if(data.handles){\r
30421                 var hs = data.handles;\r
30422                 for(var i = 0, len = hs.length; i < len; i++){\r
30423                         handles[getId(hs[i])] = data;\r
30424                 }\r
30425             }\r
30426         },\r
30427 \r
30428     /**\r
30429      * Unregister a drag drop element\r
30430      * @param {String/HTMLElement) element The id or DOM node to unregister\r
30431      */\r
30432         unregister : function(el){\r
30433             var id = getId(el, false);\r
30434             var data = elements[id];\r
30435             if(data){\r
30436                 delete elements[id];\r
30437                 if(data.handles){\r
30438                     var hs = data.handles;\r
30439                     for(var i = 0, len = hs.length; i < len; i++){\r
30440                         delete handles[getId(hs[i], false)];\r
30441                     }\r
30442                 }\r
30443             }\r
30444         },\r
30445 \r
30446     /**\r
30447      * Returns the handle registered for a DOM Node by id\r
30448      * @param {String/HTMLElement} id The DOM node or id to look up\r
30449      * @return {Object} handle The custom handle data\r
30450      */\r
30451         getHandle : function(id){\r
30452             if(typeof id != "string"){ // must be element?\r
30453                 id = id.id;\r
30454             }\r
30455             return handles[id];\r
30456         },\r
30457 \r
30458     /**\r
30459      * Returns the handle that is registered for the DOM node that is the target of the event\r
30460      * @param {Event} e The event\r
30461      * @return {Object} handle The custom handle data\r
30462      */\r
30463         getHandleFromEvent : function(e){\r
30464             var t = Ext.lib.Event.getTarget(e);\r
30465             return t ? handles[t.id] : null;\r
30466         },\r
30467 \r
30468     /**\r
30469      * Returns a custom data object that is registered for a DOM node by id\r
30470      * @param {String/HTMLElement} id The DOM node or id to look up\r
30471      * @return {Object} data The custom data\r
30472      */\r
30473         getTarget : function(id){\r
30474             if(typeof id != "string"){ // must be element?\r
30475                 id = id.id;\r
30476             }\r
30477             return elements[id];\r
30478         },\r
30479 \r
30480     /**\r
30481      * Returns a custom data object that is registered for the DOM node that is the target of the event\r
30482      * @param {Event} e The event\r
30483      * @return {Object} data The custom data\r
30484      */\r
30485         getTargetFromEvent : function(e){\r
30486             var t = Ext.lib.Event.getTarget(e);\r
30487             return t ? elements[t.id] || handles[t.id] : null;\r
30488         }\r
30489     };\r
30490 }();/**\r
30491  * @class Ext.dd.StatusProxy\r
30492  * A specialized drag proxy that supports a drop status icon, {@link Ext.Layer} styles and auto-repair.  This is the\r
30493  * default drag proxy used by all Ext.dd components.\r
30494  * @constructor\r
30495  * @param {Object} config\r
30496  */\r
30497 Ext.dd.StatusProxy = function(config){\r
30498     Ext.apply(this, config);\r
30499     this.id = this.id || Ext.id();\r
30500     this.el = new Ext.Layer({\r
30501         dh: {\r
30502             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [\r
30503                 {tag: "div", cls: "x-dd-drop-icon"},\r
30504                 {tag: "div", cls: "x-dd-drag-ghost"}\r
30505             ]\r
30506         }, \r
30507         shadow: !config || config.shadow !== false\r
30508     });\r
30509     this.ghost = Ext.get(this.el.dom.childNodes[1]);\r
30510     this.dropStatus = this.dropNotAllowed;\r
30511 };\r
30512 \r
30513 Ext.dd.StatusProxy.prototype = {\r
30514     /**\r
30515      * @cfg {String} dropAllowed\r
30516      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").\r
30517      */\r
30518     dropAllowed : "x-dd-drop-ok",\r
30519     /**\r
30520      * @cfg {String} dropNotAllowed\r
30521      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").\r
30522      */\r
30523     dropNotAllowed : "x-dd-drop-nodrop",\r
30524 \r
30525     /**\r
30526      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed\r
30527      * over the current target element.\r
30528      * @param {String} cssClass The css class for the new drop status indicator image\r
30529      */\r
30530     setStatus : function(cssClass){\r
30531         cssClass = cssClass || this.dropNotAllowed;\r
30532         if(this.dropStatus != cssClass){\r
30533             this.el.replaceClass(this.dropStatus, cssClass);\r
30534             this.dropStatus = cssClass;\r
30535         }\r
30536     },\r
30537 \r
30538     /**\r
30539      * Resets the status indicator to the default dropNotAllowed value\r
30540      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it\r
30541      */\r
30542     reset : function(clearGhost){\r
30543         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;\r
30544         this.dropStatus = this.dropNotAllowed;\r
30545         if(clearGhost){\r
30546             this.ghost.update("");\r
30547         }\r
30548     },\r
30549 \r
30550     /**\r
30551      * Updates the contents of the ghost element\r
30552      * @param {String/HTMLElement} html The html that will replace the current innerHTML of the ghost element, or a\r
30553      * DOM node to append as the child of the ghost element (in which case the innerHTML will be cleared first).\r
30554      */\r
30555     update : function(html){\r
30556         if(typeof html == "string"){\r
30557             this.ghost.update(html);\r
30558         }else{\r
30559             this.ghost.update("");\r
30560             html.style.margin = "0";\r
30561             this.ghost.dom.appendChild(html);\r
30562         }\r
30563         var el = this.ghost.dom.firstChild; \r
30564         if(el){\r
30565             Ext.fly(el).setStyle('float', 'none');\r
30566         }\r
30567     },\r
30568 \r
30569     /**\r
30570      * Returns the underlying proxy {@link Ext.Layer}\r
30571      * @return {Ext.Layer} el\r
30572     */\r
30573     getEl : function(){\r
30574         return this.el;\r
30575     },\r
30576 \r
30577     /**\r
30578      * Returns the ghost element\r
30579      * @return {Ext.Element} el\r
30580      */\r
30581     getGhost : function(){\r
30582         return this.ghost;\r
30583     },\r
30584 \r
30585     /**\r
30586      * Hides the proxy\r
30587      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them\r
30588      */\r
30589     hide : function(clear){\r
30590         this.el.hide();\r
30591         if(clear){\r
30592             this.reset(true);\r
30593         }\r
30594     },\r
30595 \r
30596     /**\r
30597      * Stops the repair animation if it's currently running\r
30598      */\r
30599     stop : function(){\r
30600         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){\r
30601             this.anim.stop();\r
30602         }\r
30603     },\r
30604 \r
30605     /**\r
30606      * Displays this proxy\r
30607      */\r
30608     show : function(){\r
30609         this.el.show();\r
30610     },\r
30611 \r
30612     /**\r
30613      * Force the Layer to sync its shadow and shim positions to the element\r
30614      */\r
30615     sync : function(){\r
30616         this.el.sync();\r
30617     },\r
30618 \r
30619     /**\r
30620      * Causes the proxy to return to its position of origin via an animation.  Should be called after an\r
30621      * invalid drop operation by the item being dragged.\r
30622      * @param {Array} xy The XY position of the element ([x, y])\r
30623      * @param {Function} callback The function to call after the repair is complete.\r
30624      * @param {Object} scope The scope (<code>this</code> reference) in which the callback function is executed. Defaults to the browser window.\r
30625      */\r
30626     repair : function(xy, callback, scope){\r
30627         this.callback = callback;\r
30628         this.scope = scope;\r
30629         if(xy && this.animRepair !== false){\r
30630             this.el.addClass("x-dd-drag-repair");\r
30631             this.el.hideUnders(true);\r
30632             this.anim = this.el.shift({\r
30633                 duration: this.repairDuration || .5,\r
30634                 easing: 'easeOut',\r
30635                 xy: xy,\r
30636                 stopFx: true,\r
30637                 callback: this.afterRepair,\r
30638                 scope: this\r
30639             });\r
30640         }else{\r
30641             this.afterRepair();\r
30642         }\r
30643     },\r
30644 \r
30645     // private\r
30646     afterRepair : function(){\r
30647         this.hide(true);\r
30648         if(typeof this.callback == "function"){\r
30649             this.callback.call(this.scope || this);\r
30650         }\r
30651         this.callback = null;\r
30652         this.scope = null;\r
30653     },\r
30654     \r
30655     destroy: function(){\r
30656         Ext.destroy(this.ghost, this.el);    \r
30657     }\r
30658 };/**\r
30659  * @class Ext.dd.DragSource\r
30660  * @extends Ext.dd.DDProxy\r
30661  * A simple class that provides the basic implementation needed to make any element draggable.\r
30662  * @constructor\r
30663  * @param {Mixed} el The container element\r
30664  * @param {Object} config\r
30665  */\r
30666 Ext.dd.DragSource = function(el, config){\r
30667     this.el = Ext.get(el);\r
30668     if(!this.dragData){\r
30669         this.dragData = {};\r
30670     }\r
30671     \r
30672     Ext.apply(this, config);\r
30673     \r
30674     if(!this.proxy){\r
30675         this.proxy = new Ext.dd.StatusProxy();\r
30676     }\r
30677     Ext.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group, \r
30678           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});\r
30679     \r
30680     this.dragging = false;\r
30681 };\r
30682 \r
30683 Ext.extend(Ext.dd.DragSource, Ext.dd.DDProxy, {\r
30684     /**\r
30685      * @cfg {String} ddGroup\r
30686      * A named drag drop group to which this object belongs.  If a group is specified, then this object will only\r
30687      * interact with other drag drop objects in the same group (defaults to undefined).\r
30688      */\r
30689     /**\r
30690      * @cfg {String} dropAllowed\r
30691      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").\r
30692      */\r
30693     dropAllowed : "x-dd-drop-ok",\r
30694     /**\r
30695      * @cfg {String} dropNotAllowed\r
30696      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").\r
30697      */\r
30698     dropNotAllowed : "x-dd-drop-nodrop",\r
30699 \r
30700     /**\r
30701      * Returns the data object associated with this drag source\r
30702      * @return {Object} data An object containing arbitrary data\r
30703      */\r
30704     getDragData : function(e){\r
30705         return this.dragData;\r
30706     },\r
30707 \r
30708     // private\r
30709     onDragEnter : function(e, id){\r
30710         var target = Ext.dd.DragDropMgr.getDDById(id);\r
30711         this.cachedTarget = target;\r
30712         if(this.beforeDragEnter(target, e, id) !== false){\r
30713             if(target.isNotifyTarget){\r
30714                 var status = target.notifyEnter(this, e, this.dragData);\r
30715                 this.proxy.setStatus(status);\r
30716             }else{\r
30717                 this.proxy.setStatus(this.dropAllowed);\r
30718             }\r
30719             \r
30720             if(this.afterDragEnter){\r
30721                 /**\r
30722                  * An empty function by default, but provided so that you can perform a custom action\r
30723                  * when the dragged item enters the drop target by providing an implementation.\r
30724                  * @param {Ext.dd.DragDrop} target The drop target\r
30725                  * @param {Event} e The event object\r
30726                  * @param {String} id The id of the dragged element\r
30727                  * @method afterDragEnter\r
30728                  */\r
30729                 this.afterDragEnter(target, e, id);\r
30730             }\r
30731         }\r
30732     },\r
30733 \r
30734     /**\r
30735      * An empty function by default, but provided so that you can perform a custom action\r
30736      * before the dragged item enters the drop target and optionally cancel the onDragEnter.\r
30737      * @param {Ext.dd.DragDrop} target The drop target\r
30738      * @param {Event} e The event object\r
30739      * @param {String} id The id of the dragged element\r
30740      * @return {Boolean} isValid True if the drag event is valid, else false to cancel\r
30741      */\r
30742     beforeDragEnter : function(target, e, id){\r
30743         return true;\r
30744     },\r
30745 \r
30746     // private\r
30747     alignElWithMouse: function() {\r
30748         Ext.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);\r
30749         this.proxy.sync();\r
30750     },\r
30751 \r
30752     // private\r
30753     onDragOver : function(e, id){\r
30754         var target = this.cachedTarget || Ext.dd.DragDropMgr.getDDById(id);\r
30755         if(this.beforeDragOver(target, e, id) !== false){\r
30756             if(target.isNotifyTarget){\r
30757                 var status = target.notifyOver(this, e, this.dragData);\r
30758                 this.proxy.setStatus(status);\r
30759             }\r
30760 \r
30761             if(this.afterDragOver){\r
30762                 /**\r
30763                  * An empty function by default, but provided so that you can perform a custom action\r
30764                  * while the dragged item is over the drop target by providing an implementation.\r
30765                  * @param {Ext.dd.DragDrop} target The drop target\r
30766                  * @param {Event} e The event object\r
30767                  * @param {String} id The id of the dragged element\r
30768                  * @method afterDragOver\r
30769                  */\r
30770                 this.afterDragOver(target, e, id);\r
30771             }\r
30772         }\r
30773     },\r
30774 \r
30775     /**\r
30776      * An empty function by default, but provided so that you can perform a custom action\r
30777      * while the dragged item is over the drop target and optionally cancel the onDragOver.\r
30778      * @param {Ext.dd.DragDrop} target The drop target\r
30779      * @param {Event} e The event object\r
30780      * @param {String} id The id of the dragged element\r
30781      * @return {Boolean} isValid True if the drag event is valid, else false to cancel\r
30782      */\r
30783     beforeDragOver : function(target, e, id){\r
30784         return true;\r
30785     },\r
30786 \r
30787     // private\r
30788     onDragOut : function(e, id){\r
30789         var target = this.cachedTarget || Ext.dd.DragDropMgr.getDDById(id);\r
30790         if(this.beforeDragOut(target, e, id) !== false){\r
30791             if(target.isNotifyTarget){\r
30792                 target.notifyOut(this, e, this.dragData);\r
30793             }\r
30794             this.proxy.reset();\r
30795             if(this.afterDragOut){\r
30796                 /**\r
30797                  * An empty function by default, but provided so that you can perform a custom action\r
30798                  * after the dragged item is dragged out of the target without dropping.\r
30799                  * @param {Ext.dd.DragDrop} target The drop target\r
30800                  * @param {Event} e The event object\r
30801                  * @param {String} id The id of the dragged element\r
30802                  * @method afterDragOut\r
30803                  */\r
30804                 this.afterDragOut(target, e, id);\r
30805             }\r
30806         }\r
30807         this.cachedTarget = null;\r
30808     },\r
30809 \r
30810     /**\r
30811      * An empty function by default, but provided so that you can perform a custom action before the dragged\r
30812      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.\r
30813      * @param {Ext.dd.DragDrop} target The drop target\r
30814      * @param {Event} e The event object\r
30815      * @param {String} id The id of the dragged element\r
30816      * @return {Boolean} isValid True if the drag event is valid, else false to cancel\r
30817      */\r
30818     beforeDragOut : function(target, e, id){\r
30819         return true;\r
30820     },\r
30821     \r
30822     // private\r
30823     onDragDrop : function(e, id){\r
30824         var target = this.cachedTarget || Ext.dd.DragDropMgr.getDDById(id);\r
30825         if(this.beforeDragDrop(target, e, id) !== false){\r
30826             if(target.isNotifyTarget){\r
30827                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?\r
30828                     this.onValidDrop(target, e, id);\r
30829                 }else{\r
30830                     this.onInvalidDrop(target, e, id);\r
30831                 }\r
30832             }else{\r
30833                 this.onValidDrop(target, e, id);\r
30834             }\r
30835             \r
30836             if(this.afterDragDrop){\r
30837                 /**\r
30838                  * An empty function by default, but provided so that you can perform a custom action\r
30839                  * after a valid drag drop has occurred by providing an implementation.\r
30840                  * @param {Ext.dd.DragDrop} target The drop target\r
30841                  * @param {Event} e The event object\r
30842                  * @param {String} id The id of the dropped element\r
30843                  * @method afterDragDrop\r
30844                  */\r
30845                 this.afterDragDrop(target, e, id);\r
30846             }\r
30847         }\r
30848         delete this.cachedTarget;\r
30849     },\r
30850 \r
30851     /**\r
30852      * An empty function by default, but provided so that you can perform a custom action before the dragged\r
30853      * item is dropped onto the target and optionally cancel the onDragDrop.\r
30854      * @param {Ext.dd.DragDrop} target The drop target\r
30855      * @param {Event} e The event object\r
30856      * @param {String} id The id of the dragged element\r
30857      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel\r
30858      */\r
30859     beforeDragDrop : function(target, e, id){\r
30860         return true;\r
30861     },\r
30862 \r
30863     // private\r
30864     onValidDrop : function(target, e, id){\r
30865         this.hideProxy();\r
30866         if(this.afterValidDrop){\r
30867             /**\r
30868              * An empty function by default, but provided so that you can perform a custom action\r
30869              * after a valid drop has occurred by providing an implementation.\r
30870              * @param {Object} target The target DD \r
30871              * @param {Event} e The event object\r
30872              * @param {String} id The id of the dropped element\r
30873              * @method afterInvalidDrop\r
30874              */\r
30875             this.afterValidDrop(target, e, id);\r
30876         }\r
30877     },\r
30878 \r
30879     // private\r
30880     getRepairXY : function(e, data){\r
30881         return this.el.getXY();  \r
30882     },\r
30883 \r
30884     // private\r
30885     onInvalidDrop : function(target, e, id){\r
30886         this.beforeInvalidDrop(target, e, id);\r
30887         if(this.cachedTarget){\r
30888             if(this.cachedTarget.isNotifyTarget){\r
30889                 this.cachedTarget.notifyOut(this, e, this.dragData);\r
30890             }\r
30891             this.cacheTarget = null;\r
30892         }\r
30893         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);\r
30894 \r
30895         if(this.afterInvalidDrop){\r
30896             /**\r
30897              * An empty function by default, but provided so that you can perform a custom action\r
30898              * after an invalid drop has occurred by providing an implementation.\r
30899              * @param {Event} e The event object\r
30900              * @param {String} id The id of the dropped element\r
30901              * @method afterInvalidDrop\r
30902              */\r
30903             this.afterInvalidDrop(e, id);\r
30904         }\r
30905     },\r
30906 \r
30907     // private\r
30908     afterRepair : function(){\r
30909         if(Ext.enableFx){\r
30910             this.el.highlight(this.hlColor || "c3daf9");\r
30911         }\r
30912         this.dragging = false;\r
30913     },\r
30914 \r
30915     /**\r
30916      * An empty function by default, but provided so that you can perform a custom action after an invalid\r
30917      * drop has occurred.\r
30918      * @param {Ext.dd.DragDrop} target The drop target\r
30919      * @param {Event} e The event object\r
30920      * @param {String} id The id of the dragged element\r
30921      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel\r
30922      */\r
30923     beforeInvalidDrop : function(target, e, id){\r
30924         return true;\r
30925     },\r
30926 \r
30927     // private\r
30928     handleMouseDown : function(e){\r
30929         if(this.dragging) {\r
30930             return;\r
30931         }\r
30932         var data = this.getDragData(e);\r
30933         if(data && this.onBeforeDrag(data, e) !== false){\r
30934             this.dragData = data;\r
30935             this.proxy.stop();\r
30936             Ext.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);\r
30937         } \r
30938     },\r
30939 \r
30940     /**\r
30941      * An empty function by default, but provided so that you can perform a custom action before the initial\r
30942      * drag event begins and optionally cancel it.\r
30943      * @param {Object} data An object containing arbitrary data to be shared with drop targets\r
30944      * @param {Event} e The event object\r
30945      * @return {Boolean} isValid True if the drag event is valid, else false to cancel\r
30946      */\r
30947     onBeforeDrag : function(data, e){\r
30948         return true;\r
30949     },\r
30950 \r
30951     /**\r
30952      * An empty function by default, but provided so that you can perform a custom action once the initial\r
30953      * drag event has begun.  The drag cannot be canceled from this function.\r
30954      * @param {Number} x The x position of the click on the dragged object\r
30955      * @param {Number} y The y position of the click on the dragged object\r
30956      */\r
30957     onStartDrag : Ext.emptyFn,\r
30958 \r
30959     // private override\r
30960     startDrag : function(x, y){\r
30961         this.proxy.reset();\r
30962         this.dragging = true;\r
30963         this.proxy.update("");\r
30964         this.onInitDrag(x, y);\r
30965         this.proxy.show();\r
30966     },\r
30967 \r
30968     // private\r
30969     onInitDrag : function(x, y){\r
30970         var clone = this.el.dom.cloneNode(true);\r
30971         clone.id = Ext.id(); // prevent duplicate ids\r
30972         this.proxy.update(clone);\r
30973         this.onStartDrag(x, y);\r
30974         return true;\r
30975     },\r
30976 \r
30977     /**\r
30978      * Returns the drag source's underlying {@link Ext.dd.StatusProxy}\r
30979      * @return {Ext.dd.StatusProxy} proxy The StatusProxy\r
30980      */\r
30981     getProxy : function(){\r
30982         return this.proxy;  \r
30983     },\r
30984 \r
30985     /**\r
30986      * Hides the drag source's {@link Ext.dd.StatusProxy}\r
30987      */\r
30988     hideProxy : function(){\r
30989         this.proxy.hide();  \r
30990         this.proxy.reset(true);\r
30991         this.dragging = false;\r
30992     },\r
30993 \r
30994     // private\r
30995     triggerCacheRefresh : function(){\r
30996         Ext.dd.DDM.refreshCache(this.groups);\r
30997     },\r
30998 \r
30999     // private - override to prevent hiding\r
31000     b4EndDrag: function(e) {\r
31001     },\r
31002 \r
31003     // private - override to prevent moving\r
31004     endDrag : function(e){\r
31005         this.onEndDrag(this.dragData, e);\r
31006     },\r
31007 \r
31008     // private\r
31009     onEndDrag : function(data, e){\r
31010     },\r
31011     \r
31012     // private - pin to cursor\r
31013     autoOffset : function(x, y) {\r
31014         this.setDelta(-12, -20);\r
31015     },\r
31016     \r
31017     destroy: function(){\r
31018         Ext.dd.DragSource.superclass.destroy.call(this);\r
31019         Ext.destroy(this.proxy);\r
31020     }\r
31021 });/**\r
31022  * @class Ext.dd.DropTarget\r
31023  * @extends Ext.dd.DDTarget\r
31024  * A simple class that provides the basic implementation needed to make any element a drop target that can have\r
31025  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.\r
31026  * @constructor\r
31027  * @param {Mixed} el The container element\r
31028  * @param {Object} config\r
31029  */\r
31030 Ext.dd.DropTarget = function(el, config){\r
31031     this.el = Ext.get(el);\r
31032     \r
31033     Ext.apply(this, config);\r
31034     \r
31035     if(this.containerScroll){\r
31036         Ext.dd.ScrollManager.register(this.el);\r
31037     }\r
31038     \r
31039     Ext.dd.DropTarget.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group, \r
31040           {isTarget: true});\r
31041 \r
31042 };\r
31043 \r
31044 Ext.extend(Ext.dd.DropTarget, Ext.dd.DDTarget, {\r
31045     /**\r
31046      * @cfg {String} ddGroup\r
31047      * A named drag drop group to which this object belongs.  If a group is specified, then this object will only\r
31048      * interact with other drag drop objects in the same group (defaults to undefined).\r
31049      */\r
31050     /**\r
31051      * @cfg {String} overClass\r
31052      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").\r
31053      */\r
31054     /**\r
31055      * @cfg {String} dropAllowed\r
31056      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").\r
31057      */\r
31058     dropAllowed : "x-dd-drop-ok",\r
31059     /**\r
31060      * @cfg {String} dropNotAllowed\r
31061      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").\r
31062      */\r
31063     dropNotAllowed : "x-dd-drop-nodrop",\r
31064 \r
31065     // private\r
31066     isTarget : true,\r
31067 \r
31068     // private\r
31069     isNotifyTarget : true,\r
31070 \r
31071     /**\r
31072      * The function a {@link Ext.dd.DragSource} calls once to notify this drop target that the source is now over the\r
31073      * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element\r
31074      * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.\r
31075      * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop target\r
31076      * @param {Event} e The event\r
31077      * @param {Object} data An object containing arbitrary data supplied by the drag source\r
31078      * @return {String} status The CSS class that communicates the drop status back to the source so that the\r
31079      * underlying {@link Ext.dd.StatusProxy} can be updated\r
31080      */\r
31081     notifyEnter : function(dd, e, data){\r
31082         if(this.overClass){\r
31083             this.el.addClass(this.overClass);\r
31084         }\r
31085         return this.dropAllowed;\r
31086     },\r
31087 \r
31088     /**\r
31089      * The function a {@link Ext.dd.DragSource} calls continuously while it is being dragged over the target.\r
31090      * This method will be called on every mouse movement while the drag source is over the drop target.\r
31091      * This default implementation simply returns the dropAllowed config value.\r
31092      * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop target\r
31093      * @param {Event} e The event\r
31094      * @param {Object} data An object containing arbitrary data supplied by the drag source\r
31095      * @return {String} status The CSS class that communicates the drop status back to the source so that the\r
31096      * underlying {@link Ext.dd.StatusProxy} can be updated\r
31097      */\r
31098     notifyOver : function(dd, e, data){\r
31099         return this.dropAllowed;\r
31100     },\r
31101 \r
31102     /**\r
31103      * The function a {@link Ext.dd.DragSource} calls once to notify this drop target that the source has been dragged\r
31104      * out of the target without dropping.  This default implementation simply removes the CSS class specified by\r
31105      * overClass (if any) from the drop element.\r
31106      * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop target\r
31107      * @param {Event} e The event\r
31108      * @param {Object} data An object containing arbitrary data supplied by the drag source\r
31109      */\r
31110     notifyOut : function(dd, e, data){\r
31111         if(this.overClass){\r
31112             this.el.removeClass(this.overClass);\r
31113         }\r
31114     },\r
31115 \r
31116     /**\r
31117      * The function a {@link Ext.dd.DragSource} calls once to notify this drop target that the dragged item has\r
31118      * been dropped on it.  This method has no default implementation and returns false, so you must provide an\r
31119      * implementation that does something to process the drop event and returns true so that the drag source's\r
31120      * repair action does not run.\r
31121      * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop target\r
31122      * @param {Event} e The event\r
31123      * @param {Object} data An object containing arbitrary data supplied by the drag source\r
31124      * @return {Boolean} True if the drop was valid, else false\r
31125      */\r
31126     notifyDrop : function(dd, e, data){\r
31127         return false;\r
31128     }\r
31129 });/**\r
31130  * @class Ext.dd.DragZone\r
31131  * @extends Ext.dd.DragSource\r
31132  * <p>This class provides a container DD instance that allows dragging of multiple child source nodes.</p>\r
31133  * <p>This class does not move the drag target nodes, but a proxy element which may contain\r
31134  * any DOM structure you wish. The DOM element to show in the proxy is provided by either a\r
31135  * provided implementation of {@link #getDragData}, or by registered draggables registered with {@link Ext.dd.Registry}</p>\r
31136  * <p>If you wish to provide draggability for an arbitrary number of DOM nodes, each of which represent some\r
31137  * application object (For example nodes in a {@link Ext.DataView DataView}) then use of this class\r
31138  * is the most efficient way to "activate" those nodes.</p>\r
31139  * <p>By default, this class requires that draggable child nodes are registered with {@link Ext.dd.Registry}.\r
31140  * However a simpler way to allow a DragZone to manage any number of draggable elements is to configure\r
31141  * the DragZone with  an implementation of the {@link #getDragData} method which interrogates the passed\r
31142  * mouse event to see if it has taken place within an element, or class of elements. This is easily done\r
31143  * by using the event's {@link Ext.EventObject#getTarget getTarget} method to identify a node based on a\r
31144  * {@link Ext.DomQuery} selector. For example, to make the nodes of a DataView draggable, use the following\r
31145  * technique. Knowledge of the use of the DataView is required:</p><pre><code>\r
31146 myDataView.on('render', function(v) {\r
31147     myDataView.dragZone = new Ext.dd.DragZone(v.getEl(), {\r
31148 \r
31149 //      On receipt of a mousedown event, see if it is within a DataView node.\r
31150 //      Return a drag data object if so.\r
31151         getDragData: function(e) {\r
31152 \r
31153 //          Use the DataView's own itemSelector (a mandatory property) to\r
31154 //          test if the mousedown is within one of the DataView's nodes.\r
31155             var sourceEl = e.getTarget(v.itemSelector, 10);\r
31156 \r
31157 //          If the mousedown is within a DataView node, clone the node to produce\r
31158 //          a ddel element for use by the drag proxy. Also add application data\r
31159 //          to the returned data object.\r
31160             if (sourceEl) {\r
31161                 d = sourceEl.cloneNode(true);\r
31162                 d.id = Ext.id();\r
31163                 return {\r
31164                     ddel: d,\r
31165                     sourceEl: sourceEl,\r
31166                     repairXY: Ext.fly(sourceEl).getXY(),\r
31167                     sourceStore: v.store,\r
31168                     draggedRecord: v.{@link Ext.DataView#getRecord getRecord}(sourceEl)\r
31169                 }\r
31170             }\r
31171         },\r
31172 \r
31173 //      Provide coordinates for the proxy to slide back to on failed drag.\r
31174 //      This is the original XY coordinates of the draggable element captured\r
31175 //      in the getDragData method.\r
31176         getRepairXY: function() {\r
31177             return this.dragData.repairXY;\r
31178         }\r
31179     });\r
31180 });</code></pre>\r
31181  * See the {@link Ext.dd.DropZone DropZone} documentation for details about building a DropZone which\r
31182  * cooperates with this DragZone.\r
31183  * @constructor\r
31184  * @param {Mixed} el The container element\r
31185  * @param {Object} config\r
31186  */\r
31187 Ext.dd.DragZone = function(el, config){\r
31188     Ext.dd.DragZone.superclass.constructor.call(this, el, config);\r
31189     if(this.containerScroll){\r
31190         Ext.dd.ScrollManager.register(this.el);\r
31191     }\r
31192 };\r
31193 \r
31194 Ext.extend(Ext.dd.DragZone, Ext.dd.DragSource, {\r
31195     /**\r
31196      * This property contains the data representing the dragged object. This data is set up by the implementation\r
31197      * of the {@link #getDragData} method. It must contain a <tt>ddel</tt> property, but can contain\r
31198      * any other data according to the application's needs.\r
31199      * @type Object\r
31200      * @property dragData\r
31201      */\r
31202     /**\r
31203      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager\r
31204      * for auto scrolling during drag operations.\r
31205      */\r
31206     /**\r
31207      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair\r
31208      * method after a failed drop (defaults to "c3daf9" - light blue)\r
31209      */\r
31210 \r
31211     /**\r
31212      * Called when a mousedown occurs in this container. Looks in {@link Ext.dd.Registry}\r
31213      * for a valid target to drag based on the mouse down. Override this method\r
31214      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned\r
31215      * object has a "ddel" attribute (with an HTML Element) for other functions to work.\r
31216      * @param {EventObject} e The mouse down event\r
31217      * @return {Object} The dragData\r
31218      */\r
31219     getDragData : function(e){\r
31220         return Ext.dd.Registry.getHandleFromEvent(e);\r
31221     },\r
31222     \r
31223     /**\r
31224      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the\r
31225      * this.dragData.ddel\r
31226      * @param {Number} x The x position of the click on the dragged object\r
31227      * @param {Number} y The y position of the click on the dragged object\r
31228      * @return {Boolean} true to continue the drag, false to cancel\r
31229      */\r
31230     onInitDrag : function(x, y){\r
31231         this.proxy.update(this.dragData.ddel.cloneNode(true));\r
31232         this.onStartDrag(x, y);\r
31233         return true;\r
31234     },\r
31235     \r
31236     /**\r
31237      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel \r
31238      */\r
31239     afterRepair : function(){\r
31240         if(Ext.enableFx){\r
31241             Ext.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");\r
31242         }\r
31243         this.dragging = false;\r
31244     },\r
31245 \r
31246     /**\r
31247      * Called before a repair of an invalid drop to get the XY to animate to. By default returns\r
31248      * the XY of this.dragData.ddel\r
31249      * @param {EventObject} e The mouse up event\r
31250      * @return {Array} The xy location (e.g. [100, 200])\r
31251      */\r
31252     getRepairXY : function(e){\r
31253         return Ext.Element.fly(this.dragData.ddel).getXY();  \r
31254     }\r
31255 });/**\r
31256  * @class Ext.dd.DropZone\r
31257  * @extends Ext.dd.DropTarget\r
31258  * <p>This class provides a container DD instance that allows dropping on multiple child target nodes.</p>\r
31259  * <p>By default, this class requires that child nodes accepting drop are registered with {@link Ext.dd.Registry}.\r
31260  * However a simpler way to allow a DropZone to manage any number of target elements is to configure the\r
31261  * DropZone with an implementation of {@link #getTargetFromEvent} which interrogates the passed\r
31262  * mouse event to see if it has taken place within an element, or class of elements. This is easily done\r
31263  * by using the event's {@link Ext.EventObject#getTarget getTarget} method to identify a node based on a\r
31264  * {@link Ext.DomQuery} selector.</p>\r
31265  * <p>Once the DropZone has detected through calling getTargetFromEvent, that the mouse is over\r
31266  * a drop target, that target is passed as the first parameter to {@link #onNodeEnter}, {@link #onNodeOver},\r
31267  * {@link #onNodeOut}, {@link #onNodeDrop}. You may configure the instance of DropZone with implementations\r
31268  * of these methods to provide application-specific behaviour for these events to update both\r
31269  * application state, and UI state.</p>\r
31270  * <p>For example to make a GridPanel a cooperating target with the example illustrated in\r
31271  * {@link Ext.dd.DragZone DragZone}, the following technique might be used:</p><pre><code>\r
31272 myGridPanel.on('render', function() {\r
31273     myGridPanel.dropZone = new Ext.dd.DropZone(myGridPanel.getView().scroller, {\r
31274 \r
31275 //      If the mouse is over a grid row, return that node. This is\r
31276 //      provided as the "target" parameter in all "onNodeXXXX" node event handling functions\r
31277         getTargetFromEvent: function(e) {\r
31278             return e.getTarget(myGridPanel.getView().rowSelector);\r
31279         },\r
31280 \r
31281 //      On entry into a target node, highlight that node.\r
31282         onNodeEnter : function(target, dd, e, data){ \r
31283             Ext.fly(target).addClass('my-row-highlight-class');\r
31284         },\r
31285 \r
31286 //      On exit from a target node, unhighlight that node.\r
31287         onNodeOut : function(target, dd, e, data){ \r
31288             Ext.fly(target).removeClass('my-row-highlight-class');\r
31289         },\r
31290 \r
31291 //      While over a target node, return the default drop allowed class which\r
31292 //      places a "tick" icon into the drag proxy.\r
31293         onNodeOver : function(target, dd, e, data){ \r
31294             return Ext.dd.DropZone.prototype.dropAllowed;\r
31295         },\r
31296 \r
31297 //      On node drop we can interrogate the target to find the underlying\r
31298 //      application object that is the real target of the dragged data.\r
31299 //      In this case, it is a Record in the GridPanel's Store.\r
31300 //      We can use the data set up by the DragZone's getDragData method to read\r
31301 //      any data we decided to attach in the DragZone's getDragData method.\r
31302         onNodeDrop : function(target, dd, e, data){\r
31303             var rowIndex = myGridPanel.getView().findRowIndex(target);\r
31304             var r = myGridPanel.getStore().getAt(rowIndex);\r
31305             Ext.Msg.alert('Drop gesture', 'Dropped Record id ' + data.draggedRecord.id +\r
31306                 ' on Record id ' + r.id);\r
31307             return true;\r
31308         }\r
31309     });\r
31310 }\r
31311 </code></pre>\r
31312  * See the {@link Ext.dd.DragZone DragZone} documentation for details about building a DragZone which\r
31313  * cooperates with this DropZone.\r
31314  * @constructor\r
31315  * @param {Mixed} el The container element\r
31316  * @param {Object} config\r
31317  */\r
31318 Ext.dd.DropZone = function(el, config){\r
31319     Ext.dd.DropZone.superclass.constructor.call(this, el, config);\r
31320 };\r
31321 \r
31322 Ext.extend(Ext.dd.DropZone, Ext.dd.DropTarget, {\r
31323     /**\r
31324      * Returns a custom data object associated with the DOM node that is the target of the event.  By default\r
31325      * this looks up the event target in the {@link Ext.dd.Registry}, although you can override this method to\r
31326      * provide your own custom lookup.\r
31327      * @param {Event} e The event\r
31328      * @return {Object} data The custom data\r
31329      */\r
31330     getTargetFromEvent : function(e){\r
31331         return Ext.dd.Registry.getTargetFromEvent(e);\r
31332     },\r
31333 \r
31334     /**\r
31335      * Called when the DropZone determines that a {@link Ext.dd.DragSource} has entered a drop node\r
31336      * that has either been registered or detected by a configured implementation of {@link #getTargetFromEvent}.\r
31337      * This method has no default implementation and should be overridden to provide\r
31338      * node-specific processing if necessary.\r
31339      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from \r
31340      * {@link #getTargetFromEvent} for this node)\r
31341      * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop zone\r
31342      * @param {Event} e The event\r
31343      * @param {Object} data An object containing arbitrary data supplied by the drag source\r
31344      */\r
31345     onNodeEnter : function(n, dd, e, data){\r
31346         \r
31347     },\r
31348 \r
31349     /**\r
31350      * Called while the DropZone determines that a {@link Ext.dd.DragSource} is over a drop node\r
31351      * that has either been registered or detected by a configured implementation of {@link #getTargetFromEvent}.\r
31352      * The default implementation returns this.dropNotAllowed, so it should be\r
31353      * overridden to provide the proper feedback.\r
31354      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from\r
31355      * {@link #getTargetFromEvent} for this node)\r
31356      * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop zone\r
31357      * @param {Event} e The event\r
31358      * @param {Object} data An object containing arbitrary data supplied by the drag source\r
31359      * @return {String} status The CSS class that communicates the drop status back to the source so that the\r
31360      * underlying {@link Ext.dd.StatusProxy} can be updated\r
31361      */\r
31362     onNodeOver : function(n, dd, e, data){\r
31363         return this.dropAllowed;\r
31364     },\r
31365 \r
31366     /**\r
31367      * Called when the DropZone determines that a {@link Ext.dd.DragSource} has been dragged out of\r
31368      * the drop node without dropping.  This method has no default implementation and should be overridden to provide\r
31369      * node-specific processing if necessary.\r
31370      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from\r
31371      * {@link #getTargetFromEvent} for this node)\r
31372      * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop zone\r
31373      * @param {Event} e The event\r
31374      * @param {Object} data An object containing arbitrary data supplied by the drag source\r
31375      */\r
31376     onNodeOut : function(n, dd, e, data){\r
31377         \r
31378     },\r
31379 \r
31380     /**\r
31381      * Called when the DropZone determines that a {@link Ext.dd.DragSource} has been dropped onto\r
31382      * the drop node.  The default implementation returns false, so it should be overridden to provide the\r
31383      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.\r
31384      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from\r
31385      * {@link #getTargetFromEvent} for this node)\r
31386      * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop zone\r
31387      * @param {Event} e The event\r
31388      * @param {Object} data An object containing arbitrary data supplied by the drag source\r
31389      * @return {Boolean} True if the drop was valid, else false\r
31390      */\r
31391     onNodeDrop : function(n, dd, e, data){\r
31392         return false;\r
31393     },\r
31394 \r
31395     /**\r
31396      * Called while the DropZone determines that a {@link Ext.dd.DragSource} is being dragged over it,\r
31397      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so\r
31398      * it should be overridden to provide the proper feedback if necessary.\r
31399      * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop zone\r
31400      * @param {Event} e The event\r
31401      * @param {Object} data An object containing arbitrary data supplied by the drag source\r
31402      * @return {String} status The CSS class that communicates the drop status back to the source so that the\r
31403      * underlying {@link Ext.dd.StatusProxy} can be updated\r
31404      */\r
31405     onContainerOver : function(dd, e, data){\r
31406         return this.dropNotAllowed;\r
31407     },\r
31408 \r
31409     /**\r
31410      * Called when the DropZone determines that a {@link Ext.dd.DragSource} has been dropped on it,\r
31411      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be\r
31412      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to\r
31413      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.\r
31414      * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop zone\r
31415      * @param {Event} e The event\r
31416      * @param {Object} data An object containing arbitrary data supplied by the drag source\r
31417      * @return {Boolean} True if the drop was valid, else false\r
31418      */\r
31419     onContainerDrop : function(dd, e, data){\r
31420         return false;\r
31421     },\r
31422 \r
31423     /**\r
31424      * The function a {@link Ext.dd.DragSource} calls once to notify this drop zone that the source is now over\r
31425      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop\r
31426      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops\r
31427      * you should override this method and provide a custom implementation.\r
31428      * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop zone\r
31429      * @param {Event} e The event\r
31430      * @param {Object} data An object containing arbitrary data supplied by the drag source\r
31431      * @return {String} status The CSS class that communicates the drop status back to the source so that the\r
31432      * underlying {@link Ext.dd.StatusProxy} can be updated\r
31433      */\r
31434     notifyEnter : function(dd, e, data){\r
31435         return this.dropNotAllowed;\r
31436     },\r
31437 \r
31438     /**\r
31439      * The function a {@link Ext.dd.DragSource} calls continuously while it is being dragged over the drop zone.\r
31440      * This method will be called on every mouse movement while the drag source is over the drop zone.\r
31441      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically\r
31442      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits\r
31443      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a\r
31444      * registered node, it will call {@link #onContainerOver}.\r
31445      * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop zone\r
31446      * @param {Event} e The event\r
31447      * @param {Object} data An object containing arbitrary data supplied by the drag source\r
31448      * @return {String} status The CSS class that communicates the drop status back to the source so that the\r
31449      * underlying {@link Ext.dd.StatusProxy} can be updated\r
31450      */\r
31451     notifyOver : function(dd, e, data){\r
31452         var n = this.getTargetFromEvent(e);\r
31453         if(!n){ // not over valid drop target\r
31454             if(this.lastOverNode){\r
31455                 this.onNodeOut(this.lastOverNode, dd, e, data);\r
31456                 this.lastOverNode = null;\r
31457             }\r
31458             return this.onContainerOver(dd, e, data);\r
31459         }\r
31460         if(this.lastOverNode != n){\r
31461             if(this.lastOverNode){\r
31462                 this.onNodeOut(this.lastOverNode, dd, e, data);\r
31463             }\r
31464             this.onNodeEnter(n, dd, e, data);\r
31465             this.lastOverNode = n;\r
31466         }\r
31467         return this.onNodeOver(n, dd, e, data);\r
31468     },\r
31469 \r
31470     /**\r
31471      * The function a {@link Ext.dd.DragSource} calls once to notify this drop zone that the source has been dragged\r
31472      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification\r
31473      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.\r
31474      * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop target\r
31475      * @param {Event} e The event\r
31476      * @param {Object} data An object containing arbitrary data supplied by the drag zone\r
31477      */\r
31478     notifyOut : function(dd, e, data){\r
31479         if(this.lastOverNode){\r
31480             this.onNodeOut(this.lastOverNode, dd, e, data);\r
31481             this.lastOverNode = null;\r
31482         }\r
31483     },\r
31484 \r
31485     /**\r
31486      * The function a {@link Ext.dd.DragSource} calls once to notify this drop zone that the dragged item has\r
31487      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there\r
31488      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,\r
31489      * otherwise it will call {@link #onContainerDrop}.\r
31490      * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop zone\r
31491      * @param {Event} e The event\r
31492      * @param {Object} data An object containing arbitrary data supplied by the drag source\r
31493      * @return {Boolean} True if the drop was valid, else false\r
31494      */\r
31495     notifyDrop : function(dd, e, data){\r
31496         if(this.lastOverNode){\r
31497             this.onNodeOut(this.lastOverNode, dd, e, data);\r
31498             this.lastOverNode = null;\r
31499         }\r
31500         var n = this.getTargetFromEvent(e);\r
31501         return n ?\r
31502             this.onNodeDrop(n, dd, e, data) :\r
31503             this.onContainerDrop(dd, e, data);\r
31504     },\r
31505 \r
31506     // private\r
31507     triggerCacheRefresh : function(){\r
31508         Ext.dd.DDM.refreshCache(this.groups);\r
31509     }  \r
31510 });/**\r
31511  * @class Ext.Element\r
31512  */\r
31513 Ext.Element.addMethods({\r
31514     /**\r
31515      * Initializes a {@link Ext.dd.DD} drag drop object for this element.\r
31516      * @param {String} group The group the DD object is member of\r
31517      * @param {Object} config The DD config object\r
31518      * @param {Object} overrides An object containing methods to override/implement on the DD object\r
31519      * @return {Ext.dd.DD} The DD object\r
31520      */\r
31521     initDD : function(group, config, overrides){\r
31522         var dd = new Ext.dd.DD(Ext.id(this.dom), group, config);\r
31523         return Ext.apply(dd, overrides);\r
31524     },\r
31525 \r
31526     /**\r
31527      * Initializes a {@link Ext.dd.DDProxy} object for this element.\r
31528      * @param {String} group The group the DDProxy object is member of\r
31529      * @param {Object} config The DDProxy config object\r
31530      * @param {Object} overrides An object containing methods to override/implement on the DDProxy object\r
31531      * @return {Ext.dd.DDProxy} The DDProxy object\r
31532      */\r
31533     initDDProxy : function(group, config, overrides){\r
31534         var dd = new Ext.dd.DDProxy(Ext.id(this.dom), group, config);\r
31535         return Ext.apply(dd, overrides);\r
31536     },\r
31537 \r
31538     /**\r
31539      * Initializes a {@link Ext.dd.DDTarget} object for this element.\r
31540      * @param {String} group The group the DDTarget object is member of\r
31541      * @param {Object} config The DDTarget config object\r
31542      * @param {Object} overrides An object containing methods to override/implement on the DDTarget object\r
31543      * @return {Ext.dd.DDTarget} The DDTarget object\r
31544      */\r
31545     initDDTarget : function(group, config, overrides){\r
31546         var dd = new Ext.dd.DDTarget(Ext.id(this.dom), group, config);\r
31547         return Ext.apply(dd, overrides);\r
31548     }\r
31549 });
31550 /**
31551  * @class Ext.data.Api
31552  * @extends Object
31553  * Ext.data.Api is a singleton designed to manage the data API including methods
31554  * for validating a developer's DataProxy API.  Defines variables for CRUD actions
31555  * create, read, update and destroy in addition to a mapping of RESTful HTTP methods
31556  * GET, POST, PUT and DELETE to CRUD actions.
31557  * @singleton
31558  */
31559 Ext.data.Api = (function() {
31560
31561     // private validActions.  validActions is essentially an inverted hash of Ext.data.Api.actions, where value becomes the key.
31562     // Some methods in this singleton (e.g.: getActions, getVerb) will loop through actions with the code <code>for (var verb in this.actions)</code>
31563     // For efficiency, some methods will first check this hash for a match.  Those methods which do acces validActions will cache their result here.
31564     // We cannot pre-define this hash since the developer may over-ride the actions at runtime.
31565     var validActions = {};
31566
31567     return {
31568         /**
31569          * Defined actions corresponding to remote actions:
31570          * <pre><code>
31571 actions: {
31572     create  : 'create',  // Text representing the remote-action to create records on server.
31573     read    : 'read',    // Text representing the remote-action to read/load data from server.
31574     update  : 'update',  // Text representing the remote-action to update records on server.
31575     destroy : 'destroy'  // Text representing the remote-action to destroy records on server.
31576 }
31577          * </code></pre>
31578          * @property actions
31579          * @type Object
31580          */
31581         actions : {
31582             create  : 'create',
31583             read    : 'read',
31584             update  : 'update',
31585             destroy : 'destroy'
31586         },
31587
31588         /**
31589          * Defined {CRUD action}:{HTTP method} pairs to associate HTTP methods with the
31590          * corresponding actions for {@link Ext.data.DataProxy#restful RESTful proxies}.
31591          * Defaults to:
31592          * <pre><code>
31593 restActions : {
31594     create  : 'POST',
31595     read    : 'GET',
31596     update  : 'PUT',
31597     destroy : 'DELETE'
31598 },
31599          * </code></pre>
31600          */
31601         restActions : {
31602             create  : 'POST',
31603             read    : 'GET',
31604             update  : 'PUT',
31605             destroy : 'DELETE'
31606         },
31607
31608         /**
31609          * Returns true if supplied action-name is a valid API action defined in <code>{@link #actions}</code> constants
31610          * @param {String} action
31611          * @param {String[]}(Optional) List of available CRUD actions.  Pass in list when executing multiple times for efficiency.
31612          * @return {Boolean}
31613          */
31614         isAction : function(action) {
31615             return (Ext.data.Api.actions[action]) ? true : false;
31616         },
31617
31618         /**
31619          * Returns the actual CRUD action KEY "create", "read", "update" or "destroy" from the supplied action-name.  This method is used internally and shouldn't generally
31620          * need to be used directly.  The key/value pair of Ext.data.Api.actions will often be identical but this is not necessarily true.  A developer can override this naming
31621          * convention if desired.  However, the framework internally calls methods based upon the KEY so a way of retreiving the the words "create", "read", "update" and "destroy" is
31622          * required.  This method will cache discovered KEYS into the private validActions hash.
31623          * @param {String} name The runtime name of the action.
31624          * @return {String||null} returns the action-key, or verb of the user-action or null if invalid.
31625          * @nodoc
31626          */
31627         getVerb : function(name) {
31628             if (validActions[name]) {
31629                 return validActions[name];  // <-- found in cache.  return immediately.
31630             }
31631             for (var verb in this.actions) {
31632                 if (this.actions[verb] === name) {
31633                     validActions[name] = verb;
31634                     break;
31635                 }
31636             }
31637             return (validActions[name] !== undefined) ? validActions[name] : null;
31638         },
31639
31640         /**
31641          * Returns true if the supplied API is valid; that is, check that all keys match defined actions
31642          * otherwise returns an array of mistakes.
31643          * @return {String[]||true}
31644          */
31645         isValid : function(api){
31646             var invalid = [];
31647             var crud = this.actions; // <-- cache a copy of the actions.
31648             for (var action in api) {
31649                 if (!(action in crud)) {
31650                     invalid.push(action);
31651                 }
31652             }
31653             return (!invalid.length) ? true : invalid;
31654         },
31655
31656         /**
31657          * Returns true if the supplied verb upon the supplied proxy points to a unique url in that none of the other api-actions
31658          * point to the same url.  The question is important for deciding whether to insert the "xaction" HTTP parameter within an
31659          * Ajax request.  This method is used internally and shouldn't generally need to be called directly.
31660          * @param {Ext.data.DataProxy} proxy
31661          * @param {String} verb
31662          * @return {Boolean}
31663          */
31664         hasUniqueUrl : function(proxy, verb) {
31665             var url = (proxy.api[verb]) ? proxy.api[verb].url : null;
31666             var unique = true;
31667             for (var action in proxy.api) {
31668                 if ((unique = (action === verb) ? true : (proxy.api[action].url != url) ? true : false) === false) {
31669                     break;
31670                 }
31671             }
31672             return unique;
31673         },
31674
31675         /**
31676          * This method is used internally by <tt>{@link Ext.data.DataProxy DataProxy}</tt> and should not generally need to be used directly.
31677          * Each action of a DataProxy api can be initially defined as either a String or an Object.  When specified as an object,
31678          * one can explicitly define the HTTP method (GET|POST) to use for each CRUD action.  This method will prepare the supplied API, setting
31679          * each action to the Object form.  If your API-actions do not explicitly define the HTTP method, the "method" configuration-parameter will
31680          * be used.  If the method configuration parameter is not specified, POST will be used.
31681          <pre><code>
31682 new Ext.data.HttpProxy({
31683     method: "POST",     // <-- default HTTP method when not specified.
31684     api: {
31685         create: 'create.php',
31686         load: 'read.php',
31687         save: 'save.php',
31688         destroy: 'destroy.php'
31689     }
31690 });
31691
31692 // Alternatively, one can use the object-form to specify the API
31693 new Ext.data.HttpProxy({
31694     api: {
31695         load: {url: 'read.php', method: 'GET'},
31696         create: 'create.php',
31697         destroy: 'destroy.php',
31698         save: 'update.php'
31699     }
31700 });
31701         </code></pre>
31702          *
31703          * @param {Ext.data.DataProxy} proxy
31704          */
31705         prepare : function(proxy) {
31706             if (!proxy.api) {
31707                 proxy.api = {}; // <-- No api?  create a blank one.
31708             }
31709             for (var verb in this.actions) {
31710                 var action = this.actions[verb];
31711                 proxy.api[action] = proxy.api[action] || proxy.url || proxy.directFn;
31712                 if (typeof(proxy.api[action]) == 'string') {
31713                     proxy.api[action] = {
31714                         url: proxy.api[action],
31715                         method: (proxy.restful === true) ? Ext.data.Api.restActions[action] : undefined
31716                     };
31717                 }
31718             }
31719         },
31720
31721         /**
31722          * Prepares a supplied Proxy to be RESTful.  Sets the HTTP method for each api-action to be one of
31723          * GET, POST, PUT, DELETE according to the defined {@link #restActions}.
31724          * @param {Ext.data.DataProxy} proxy
31725          */
31726         restify : function(proxy) {
31727             proxy.restful = true;
31728             for (var verb in this.restActions) {
31729                 proxy.api[this.actions[verb]].method ||
31730                     (proxy.api[this.actions[verb]].method = this.restActions[verb]);
31731             }
31732             // TODO: perhaps move this interceptor elsewhere?  like into DataProxy, perhaps?  Placed here
31733             // to satisfy initial 3.0 final release of REST features.
31734             proxy.onWrite = proxy.onWrite.createInterceptor(function(action, o, response, rs) {
31735                 var reader = o.reader;
31736                 var res = new Ext.data.Response({
31737                     action: action,
31738                     raw: response
31739                 });
31740
31741                 switch (response.status) {
31742                     case 200:   // standard 200 response, send control back to HttpProxy#onWrite by returning true from this intercepted #onWrite
31743                         return true;
31744                         break;
31745                     case 201:   // entity created but no response returned
31746                         if (Ext.isEmpty(res.raw.responseText)) {
31747                           res.success = true;
31748                         } else {
31749                           //if the response contains data, treat it like a 200
31750                           return true;
31751                         }
31752                         break;
31753                     case 204:  // no-content.  Create a fake response.
31754                         res.success = true;
31755                         res.data = null;
31756                         break;
31757                     default:
31758                         return true;
31759                         break;
31760                 }
31761                 if (res.success === true) {
31762                     this.fireEvent("write", this, action, res.data, res, rs, o.request.arg);
31763                 } else {
31764                     this.fireEvent('exception', this, 'remote', action, o, res, rs);
31765                 }
31766                 o.request.callback.call(o.request.scope, res.data, res, res.success);
31767
31768                 return false;   // <-- false to prevent intercepted function from running.
31769             }, proxy);
31770         }
31771     };
31772 })();
31773
31774 /**
31775  * Ext.data.Response
31776  * Experimental.  Do not use directly.
31777  */
31778 Ext.data.Response = function(params, response) {
31779     Ext.apply(this, params, {
31780         raw: response
31781     });
31782 };
31783 Ext.data.Response.prototype = {
31784     message : null,
31785     success : false,
31786     status : null,
31787     root : null,
31788     raw : null,
31789
31790     getMessage : function() {
31791         return this.message;
31792     },
31793     getSuccess : function() {
31794         return this.success;
31795     },
31796     getStatus : function() {
31797         return this.status;
31798     },
31799     getRoot : function() {
31800         return this.root;
31801     },
31802     getRawResponse : function() {
31803         return this.raw;
31804     }
31805 };
31806
31807 /**
31808  * @class Ext.data.Api.Error
31809  * @extends Ext.Error
31810  * Error class for Ext.data.Api errors
31811  */
31812 Ext.data.Api.Error = Ext.extend(Ext.Error, {
31813     constructor : function(message, arg) {
31814         this.arg = arg;
31815         Ext.Error.call(this, message);
31816     },
31817     name: 'Ext.data.Api'
31818 });
31819 Ext.apply(Ext.data.Api.Error.prototype, {
31820     lang: {
31821         'action-url-undefined': 'No fallback url defined for this action.  When defining a DataProxy api, please be sure to define an url for each CRUD action in Ext.data.Api.actions or define a default url in addition to your api-configuration.',
31822         'invalid': 'received an invalid API-configuration.  Please ensure your proxy API-configuration contains only the actions defined in Ext.data.Api.actions',
31823         'invalid-url': 'Invalid url.  Please review your proxy configuration.',
31824         'execute': 'Attempted to execute an unknown action.  Valid API actions are defined in Ext.data.Api.actions"'
31825     }
31826 });
31827
31828
31829 \r
31830 /**\r
31831  * @class Ext.data.SortTypes\r
31832  * @singleton\r
31833  * Defines the default sorting (casting?) comparison functions used when sorting data.\r
31834  */\r
31835 Ext.data.SortTypes = {\r
31836     /**\r
31837      * Default sort that does nothing\r
31838      * @param {Mixed} s The value being converted\r
31839      * @return {Mixed} The comparison value\r
31840      */\r
31841     none : function(s){\r
31842         return s;\r
31843     },\r
31844     \r
31845     /**\r
31846      * The regular expression used to strip tags\r
31847      * @type {RegExp}\r
31848      * @property\r
31849      */\r
31850     stripTagsRE : /<\/?[^>]+>/gi,\r
31851     \r
31852     /**\r
31853      * Strips all HTML tags to sort on text only\r
31854      * @param {Mixed} s The value being converted\r
31855      * @return {String} The comparison value\r
31856      */\r
31857     asText : function(s){\r
31858         return String(s).replace(this.stripTagsRE, "");\r
31859     },\r
31860     \r
31861     /**\r
31862      * Strips all HTML tags to sort on text only - Case insensitive\r
31863      * @param {Mixed} s The value being converted\r
31864      * @return {String} The comparison value\r
31865      */\r
31866     asUCText : function(s){\r
31867         return String(s).toUpperCase().replace(this.stripTagsRE, "");\r
31868     },\r
31869     \r
31870     /**\r
31871      * Case insensitive string\r
31872      * @param {Mixed} s The value being converted\r
31873      * @return {String} The comparison value\r
31874      */\r
31875     asUCString : function(s) {\r
31876         return String(s).toUpperCase();\r
31877     },\r
31878     \r
31879     /**\r
31880      * Date sorting\r
31881      * @param {Mixed} s The value being converted\r
31882      * @return {Number} The comparison value\r
31883      */\r
31884     asDate : function(s) {\r
31885         if(!s){\r
31886             return 0;\r
31887         }\r
31888         if(Ext.isDate(s)){\r
31889             return s.getTime();\r
31890         }\r
31891         return Date.parse(String(s));\r
31892     },\r
31893     \r
31894     /**\r
31895      * Float sorting\r
31896      * @param {Mixed} s The value being converted\r
31897      * @return {Float} The comparison value\r
31898      */\r
31899     asFloat : function(s) {\r
31900         var val = parseFloat(String(s).replace(/,/g, ""));\r
31901         return isNaN(val) ? 0 : val;\r
31902     },\r
31903     \r
31904     /**\r
31905      * Integer sorting\r
31906      * @param {Mixed} s The value being converted\r
31907      * @return {Number} The comparison value\r
31908      */\r
31909     asInt : function(s) {\r
31910         var val = parseInt(String(s).replace(/,/g, ""), 10);\r
31911         return isNaN(val) ? 0 : val;\r
31912     }\r
31913 };/**
31914  * @class Ext.data.Record
31915  * <p>Instances of this class encapsulate both Record <em>definition</em> information, and Record
31916  * <em>value</em> information for use in {@link Ext.data.Store} objects, or any code which needs
31917  * to access Records cached in an {@link Ext.data.Store} object.</p>
31918  * <p>Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
31919  * Instances are usually only created by {@link Ext.data.Reader} implementations when processing unformatted data
31920  * objects.</p>
31921  * <p>Note that an instance of a Record class may only belong to one {@link Ext.data.Store Store} at a time.
31922  * In order to copy data from one Store to another, use the {@link #copy} method to create an exact
31923  * copy of the Record, and insert the new instance into the other Store.</p>
31924  * <p>When serializing a Record for submission to the server, be aware that it contains many private
31925  * properties, and also a reference to its owning Store which in turn holds references to its Records.
31926  * This means that a whole Record may not be encoded using {@link Ext.util.JSON.encode}. Instead, use the
31927  * <code>{@link #data}</code> and <code>{@link #id}</code> properties.</p>
31928  * <p>Record objects generated by this constructor inherit all the methods of Ext.data.Record listed below.</p>
31929  * @constructor
31930  * <p>This constructor should not be used to create Record objects. Instead, use {@link #create} to
31931  * generate a subclass of Ext.data.Record configured with information about its constituent fields.<p>
31932  * <p><b>The generated constructor has the same signature as this constructor.</b></p>
31933  * @param {Object} data (Optional) An object, the properties of which provide values for the new Record's
31934  * fields. If not specified the <code>{@link Ext.data.Field#defaultValue defaultValue}</code>
31935  * for each field will be assigned.
31936  * @param {Object} id (Optional) The id of the Record. The id is used by the
31937  * {@link Ext.data.Store} object which owns the Record to index its collection
31938  * of Records (therefore this id should be unique within each store). If an
31939  * <code>id</code> is not specified a <b><code>{@link #phantom}</code></b>
31940  * Record will be created with an {@link #Record.id automatically generated id}.
31941  */
31942 Ext.data.Record = function(data, id){
31943     // if no id, call the auto id method
31944     this.id = (id || id === 0) ? id : Ext.data.Record.id(this);
31945     this.data = data || {};
31946 };
31947
31948 /**
31949  * Generate a constructor for a specific Record layout.
31950  * @param {Array} o An Array of <b>{@link Ext.data.Field Field}</b> definition objects.
31951  * The constructor generated by this method may be used to create new Record instances. The data
31952  * object must contain properties named after the {@link Ext.data.Field field}
31953  * <b><tt>{@link Ext.data.Field#name}s</tt></b>.  Example usage:<pre><code>
31954 // create a Record constructor from a description of the fields
31955 var TopicRecord = Ext.data.Record.create([ // creates a subclass of Ext.data.Record
31956     {{@link Ext.data.Field#name name}: 'title', {@link Ext.data.Field#mapping mapping}: 'topic_title'},
31957     {name: 'author', mapping: 'username', allowBlank: false},
31958     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
31959     {name: 'lastPost', mapping: 'post_time', type: 'date'},
31960     {name: 'lastPoster', mapping: 'user2'},
31961     {name: 'excerpt', mapping: 'post_text', allowBlank: false},
31962     // In the simplest case, if no properties other than <tt>name</tt> are required,
31963     // a field definition may consist of just a String for the field name.
31964     'signature'
31965 ]);
31966
31967 // create Record instance
31968 var myNewRecord = new TopicRecord(
31969     {
31970         title: 'Do my job please',
31971         author: 'noobie',
31972         totalPosts: 1,
31973         lastPost: new Date(),
31974         lastPoster: 'Animal',
31975         excerpt: 'No way dude!',
31976         signature: ''
31977     },
31978     id // optionally specify the id of the record otherwise {@link #Record.id one is auto-assigned}
31979 );
31980 myStore.{@link Ext.data.Store#add add}(myNewRecord);
31981 </code></pre>
31982  * @method create
31983  * @return {function} A constructor which is used to create new Records according
31984  * to the definition. The constructor has the same signature as {@link #Record}.
31985  * @static
31986  */
31987 Ext.data.Record.create = function(o){
31988     var f = Ext.extend(Ext.data.Record, {});
31989     var p = f.prototype;
31990     p.fields = new Ext.util.MixedCollection(false, function(field){
31991         return field.name;
31992     });
31993     for(var i = 0, len = o.length; i < len; i++){
31994         p.fields.add(new Ext.data.Field(o[i]));
31995     }
31996     f.getField = function(name){
31997         return p.fields.get(name);
31998     };
31999     return f;
32000 };
32001
32002 Ext.data.Record.PREFIX = 'ext-record';
32003 Ext.data.Record.AUTO_ID = 1;
32004 Ext.data.Record.EDIT = 'edit';
32005 Ext.data.Record.REJECT = 'reject';
32006 Ext.data.Record.COMMIT = 'commit';
32007
32008
32009 /**
32010  * Generates a sequential id. This method is typically called when a record is {@link #create}d
32011  * and {@link #Record no id has been specified}. The returned id takes the form:
32012  * <tt>&#123;PREFIX}-&#123;AUTO_ID}</tt>.<div class="mdetail-params"><ul>
32013  * <li><b><tt>PREFIX</tt></b> : String<p class="sub-desc"><tt>Ext.data.Record.PREFIX</tt>
32014  * (defaults to <tt>'ext-record'</tt>)</p></li>
32015  * <li><b><tt>AUTO_ID</tt></b> : String<p class="sub-desc"><tt>Ext.data.Record.AUTO_ID</tt>
32016  * (defaults to <tt>1</tt> initially)</p></li>
32017  * </ul></div>
32018  * @param {Record} rec The record being created.  The record does not exist, it's a {@link #phantom}.
32019  * @return {String} auto-generated string id, <tt>"ext-record-i++'</tt>;
32020  */
32021 Ext.data.Record.id = function(rec) {
32022     rec.phantom = true;
32023     return [Ext.data.Record.PREFIX, '-', Ext.data.Record.AUTO_ID++].join('');
32024 };
32025
32026 Ext.data.Record.prototype = {
32027     /**
32028      * <p><b>This property is stored in the Record definition's <u>prototype</u></b></p>
32029      * A MixedCollection containing the defined {@link Ext.data.Field Field}s for this Record.  Read-only.
32030      * @property fields
32031      * @type Ext.util.MixedCollection
32032      */
32033     /**
32034      * An object hash representing the data for this Record. Every field name in the Record definition
32035      * is represented by a property of that name in this object. Note that unless you specified a field
32036      * with {@link Ext.data.Field#name name} "id" in the Record definition, this will <b>not</b> contain
32037      * an <tt>id</tt> property.
32038      * @property data
32039      * @type {Object}
32040      */
32041     /**
32042      * The unique ID of the Record {@link #Record as specified at construction time}.
32043      * @property id
32044      * @type {Object}
32045      */
32046     /**
32047      * <p><b>Only present if this Record was created by an {@link Ext.data.XmlReader XmlReader}</b>.</p>
32048      * <p>The XML element which was the source of the data for this Record.</p>
32049      * @property node
32050      * @type {XMLElement}
32051      */
32052     /**
32053      * <p><b>Only present if this Record was created by an {@link Ext.data.ArrayReader ArrayReader} or a {@link Ext.data.JsonReader JsonReader}</b>.</p>
32054      * <p>The Array or object which was the source of the data for this Record.</p>
32055      * @property json
32056      * @type {Array|Object}
32057      */
32058     /**
32059      * Readonly flag - true if this Record has been modified.
32060      * @type Boolean
32061      */
32062     dirty : false,
32063     editing : false,
32064     error : null,
32065     /**
32066      * This object contains a key and value storing the original values of all modified
32067      * fields or is null if no fields have been modified.
32068      * @property modified
32069      * @type {Object}
32070      */
32071     modified : null,
32072     /**
32073      * <tt>true</tt> when the record does not yet exist in a server-side database (see
32074      * {@link #markDirty}).  Any record which has a real database pk set as its id property
32075      * is NOT a phantom -- it's real.
32076      * @property phantom
32077      * @type {Boolean}
32078      */
32079     phantom : false,
32080
32081     // private
32082     join : function(store){
32083         /**
32084          * The {@link Ext.data.Store} to which this Record belongs.
32085          * @property store
32086          * @type {Ext.data.Store}
32087          */
32088         this.store = store;
32089     },
32090
32091     /**
32092      * Set the {@link Ext.data.Field#name named field} to the specified value.  For example:
32093      * <pre><code>
32094 // record has a field named 'firstname'
32095 var Employee = Ext.data.Record.{@link #create}([
32096     {name: 'firstname'},
32097     ...
32098 ]);
32099
32100 // update the 2nd record in the store:
32101 var rec = myStore.{@link Ext.data.Store#getAt getAt}(1);
32102
32103 // set the value (shows dirty flag):
32104 rec.set('firstname', 'Betty');
32105
32106 // commit the change (removes dirty flag):
32107 rec.{@link #commit}();
32108
32109 // update the record in the store, bypass setting dirty flag,
32110 // and do not store the change in the {@link Ext.data.Store#getModifiedRecords modified records}
32111 rec.{@link #data}['firstname'] = 'Wilma'; // updates record, but not the view
32112 rec.{@link #commit}(); // updates the view
32113      * </code></pre>
32114      * <b>Notes</b>:<div class="mdetail-params"><ul>
32115      * <li>If the store has a writer and <code>autoSave=true</code>, each set()
32116      * will execute an XHR to the server.</li>
32117      * <li>Use <code>{@link #beginEdit}</code> to prevent the store's <code>update</code>
32118      * event firing while using set().</li>
32119      * <li>Use <code>{@link #endEdit}</code> to have the store's <code>update</code>
32120      * event fire.</li>
32121      * </ul></div>
32122      * @param {String} name The {@link Ext.data.Field#name name of the field} to set.
32123      * @param {String/Object/Array} value The value to set the field to.
32124      */
32125     set : function(name, value){
32126         var encode = Ext.isPrimitive(value) ? String : Ext.encode;
32127         if(encode(this.data[name]) == encode(value)) {
32128             return;
32129         }        
32130         this.dirty = true;
32131         if(!this.modified){
32132             this.modified = {};
32133         }
32134         if(this.modified[name] === undefined){
32135             this.modified[name] = this.data[name];
32136         }
32137         this.data[name] = value;
32138         if(!this.editing){
32139             this.afterEdit();
32140         }
32141     },
32142
32143     // private
32144     afterEdit : function(){
32145         if(this.store){
32146             this.store.afterEdit(this);
32147         }
32148     },
32149
32150     // private
32151     afterReject : function(){
32152         if(this.store){
32153             this.store.afterReject(this);
32154         }
32155     },
32156
32157     // private
32158     afterCommit : function(){
32159         if(this.store){
32160             this.store.afterCommit(this);
32161         }
32162     },
32163
32164     /**
32165      * Get the value of the {@link Ext.data.Field#name named field}.
32166      * @param {String} name The {@link Ext.data.Field#name name of the field} to get the value of.
32167      * @return {Object} The value of the field.
32168      */
32169     get : function(name){
32170         return this.data[name];
32171     },
32172
32173     /**
32174      * Begin an edit. While in edit mode, no events (e.g.. the <code>update</code> event)
32175      * are relayed to the containing store.
32176      * See also: <code>{@link #endEdit}</code> and <code>{@link #cancelEdit}</code>.
32177      */
32178     beginEdit : function(){
32179         this.editing = true;
32180         this.modified = this.modified || {};
32181     },
32182
32183     /**
32184      * Cancels all changes made in the current edit operation.
32185      */
32186     cancelEdit : function(){
32187         this.editing = false;
32188         delete this.modified;
32189     },
32190
32191     /**
32192      * End an edit. If any data was modified, the containing store is notified
32193      * (ie, the store's <code>update</code> event will fire).
32194      */
32195     endEdit : function(){
32196         this.editing = false;
32197         if(this.dirty){
32198             this.afterEdit();
32199         }
32200     },
32201
32202     /**
32203      * Usually called by the {@link Ext.data.Store} which owns the Record.
32204      * Rejects all changes made to the Record since either creation, or the last commit operation.
32205      * Modified fields are reverted to their original values.
32206      * <p>Developers should subscribe to the {@link Ext.data.Store#update} event
32207      * to have their code notified of reject operations.</p>
32208      * @param {Boolean} silent (optional) True to skip notification of the owning
32209      * store of the change (defaults to false)
32210      */
32211     reject : function(silent){
32212         var m = this.modified;
32213         for(var n in m){
32214             if(typeof m[n] != "function"){
32215                 this.data[n] = m[n];
32216             }
32217         }
32218         this.dirty = false;
32219         delete this.modified;
32220         this.editing = false;
32221         if(silent !== true){
32222             this.afterReject();
32223         }
32224     },
32225
32226     /**
32227      * Usually called by the {@link Ext.data.Store} which owns the Record.
32228      * Commits all changes made to the Record since either creation, or the last commit operation.
32229      * <p>Developers should subscribe to the {@link Ext.data.Store#update} event
32230      * to have their code notified of commit operations.</p>
32231      * @param {Boolean} silent (optional) True to skip notification of the owning
32232      * store of the change (defaults to false)
32233      */
32234     commit : function(silent){
32235         this.dirty = false;
32236         delete this.modified;
32237         this.editing = false;
32238         if(silent !== true){
32239             this.afterCommit();
32240         }
32241     },
32242
32243     /**
32244      * Gets a hash of only the fields that have been modified since this Record was created or commited.
32245      * @return Object
32246      */
32247     getChanges : function(){
32248         var m = this.modified, cs = {};
32249         for(var n in m){
32250             if(m.hasOwnProperty(n)){
32251                 cs[n] = this.data[n];
32252             }
32253         }
32254         return cs;
32255     },
32256
32257     // private
32258     hasError : function(){
32259         return this.error !== null;
32260     },
32261
32262     // private
32263     clearError : function(){
32264         this.error = null;
32265     },
32266
32267     /**
32268      * Creates a copy (clone) of this Record.
32269      * @param {String} id (optional) A new Record id, defaults to the id
32270      * of the record being copied. See <code>{@link #id}</code>. 
32271      * To generate a phantom record with a new id use:<pre><code>
32272 var rec = record.copy(); // clone the record
32273 Ext.data.Record.id(rec); // automatically generate a unique sequential id
32274      * </code></pre>
32275      * @return {Record}
32276      */
32277     copy : function(newId) {
32278         return new this.constructor(Ext.apply({}, this.data), newId || this.id);
32279     },
32280
32281     /**
32282      * Returns <tt>true</tt> if the passed field name has been <code>{@link #modified}</code>
32283      * since the load or last commit.
32284      * @param {String} fieldName {@link Ext.data.Field.{@link Ext.data.Field#name}
32285      * @return {Boolean}
32286      */
32287     isModified : function(fieldName){
32288         return !!(this.modified && this.modified.hasOwnProperty(fieldName));
32289     },
32290
32291     /**
32292      * By default returns <tt>false</tt> if any {@link Ext.data.Field field} within the
32293      * record configured with <tt>{@link Ext.data.Field#allowBlank} = false</tt> returns
32294      * <tt>true</tt> from an {@link Ext}.{@link Ext#isEmpty isempty} test.
32295      * @return {Boolean}
32296      */
32297     isValid : function() {
32298         return this.fields.find(function(f) {
32299             return (f.allowBlank === false && Ext.isEmpty(this.data[f.name])) ? true : false;
32300         },this) ? false : true;
32301     },
32302
32303     /**
32304      * <p>Marks this <b>Record</b> as <code>{@link #dirty}</code>.  This method
32305      * is used interally when adding <code>{@link #phantom}</code> records to a
32306      * {@link Ext.data.Store#writer writer enabled store}.</p>
32307      * <br><p>Marking a record <code>{@link #dirty}</code> causes the phantom to
32308      * be returned by {@link Ext.data.Store#getModifiedRecords} where it will
32309      * have a create action composed for it during {@link Ext.data.Store#save store save}
32310      * operations.</p>
32311      */
32312     markDirty : function(){
32313         this.dirty = true;
32314         if(!this.modified){
32315             this.modified = {};
32316         }
32317         this.fields.each(function(f) {
32318             this.modified[f.name] = this.data[f.name];
32319         },this);
32320     }
32321 };
32322 /**
32323  * @class Ext.StoreMgr
32324  * @extends Ext.util.MixedCollection
32325  * The default global group of stores.
32326  * @singleton
32327  */
32328 Ext.StoreMgr = Ext.apply(new Ext.util.MixedCollection(), {
32329     /**
32330      * @cfg {Object} listeners @hide
32331      */
32332
32333     /**
32334      * Registers one or more Stores with the StoreMgr. You do not normally need to register stores
32335      * manually.  Any store initialized with a {@link Ext.data.Store#storeId} will be auto-registered. 
32336      * @param {Ext.data.Store} store1 A Store instance
32337      * @param {Ext.data.Store} store2 (optional)
32338      * @param {Ext.data.Store} etc... (optional)
32339      */
32340     register : function(){
32341         for(var i = 0, s; (s = arguments[i]); i++){
32342             this.add(s);
32343         }
32344     },
32345
32346     /**
32347      * Unregisters one or more Stores with the StoreMgr
32348      * @param {String/Object} id1 The id of the Store, or a Store instance
32349      * @param {String/Object} id2 (optional)
32350      * @param {String/Object} etc... (optional)
32351      */
32352     unregister : function(){
32353         for(var i = 0, s; (s = arguments[i]); i++){
32354             this.remove(this.lookup(s));
32355         }
32356     },
32357
32358     /**
32359      * Gets a registered Store by id
32360      * @param {String/Object} id The id of the Store, or a Store instance
32361      * @return {Ext.data.Store}
32362      */
32363     lookup : function(id){
32364         if(Ext.isArray(id)){
32365             var fields = ['field1'], expand = !Ext.isArray(id[0]);
32366             if(!expand){
32367                 for(var i = 2, len = id[0].length; i <= len; ++i){
32368                     fields.push('field' + i);
32369                 }
32370             }
32371             return new Ext.data.ArrayStore({
32372                 fields: fields,
32373                 data: id,
32374                 expandData: expand,
32375                 autoDestroy: true,
32376                 autoCreated: true
32377
32378             });
32379         }
32380         return Ext.isObject(id) ? (id.events ? id : Ext.create(id, 'store')) : this.get(id);
32381     },
32382
32383     // getKey implementation for MixedCollection
32384     getKey : function(o){
32385          return o.storeId;
32386     }
32387 });/**
32388  * @class Ext.data.Store
32389  * @extends Ext.util.Observable
32390  * <p>The Store class encapsulates a client side cache of {@link Ext.data.Record Record}
32391  * objects which provide input data for Components such as the {@link Ext.grid.GridPanel GridPanel},
32392  * the {@link Ext.form.ComboBox ComboBox}, or the {@link Ext.DataView DataView}.</p>
32393  * <p><u>Retrieving Data</u></p>
32394  * <p>A Store object may access a data object using:<div class="mdetail-params"><ul>
32395  * <li>{@link #proxy configured implementation} of {@link Ext.data.DataProxy DataProxy}</li>
32396  * <li>{@link #data} to automatically pass in data</li>
32397  * <li>{@link #loadData} to manually pass in data</li>
32398  * </ul></div></p>
32399  * <p><u>Reading Data</u></p>
32400  * <p>A Store object has no inherent knowledge of the format of the data object (it could be
32401  * an Array, XML, or JSON). A Store object uses an appropriate {@link #reader configured implementation}
32402  * of a {@link Ext.data.DataReader DataReader} to create {@link Ext.data.Record Record} instances from the data
32403  * object.</p>
32404  * <p><u>Store Types</u></p>
32405  * <p>There are several implementations of Store available which are customized for use with
32406  * a specific DataReader implementation.  Here is an example using an ArrayStore which implicitly
32407  * creates a reader commensurate to an Array data object.</p>
32408  * <pre><code>
32409 var myStore = new Ext.data.ArrayStore({
32410     fields: ['fullname', 'first'],
32411     idIndex: 0 // id for each record will be the first element
32412 });
32413  * </code></pre>
32414  * <p>For custom implementations create a basic {@link Ext.data.Store} configured as needed:</p>
32415  * <pre><code>
32416 // create a {@link Ext.data.Record Record} constructor:
32417 var rt = Ext.data.Record.create([
32418     {name: 'fullname'},
32419     {name: 'first'}
32420 ]);
32421 var myStore = new Ext.data.Store({
32422     // explicitly create reader
32423     reader: new Ext.data.ArrayReader(
32424         {
32425             idIndex: 0  // id for each record will be the first element
32426         },
32427         rt // recordType
32428     )
32429 });
32430  * </code></pre>
32431  * <p>Load some data into store (note the data object is an array which corresponds to the reader):</p>
32432  * <pre><code>
32433 var myData = [
32434     [1, 'Fred Flintstone', 'Fred'],  // note that id for the record is the first element
32435     [2, 'Barney Rubble', 'Barney']
32436 ];
32437 myStore.loadData(myData);
32438  * </code></pre>
32439  * <p>Records are cached and made available through accessor functions.  An example of adding
32440  * a record to the store:</p>
32441  * <pre><code>
32442 var defaultData = {
32443     fullname: 'Full Name',
32444     first: 'First Name'
32445 };
32446 var recId = 100; // provide unique id for the record
32447 var r = new myStore.recordType(defaultData, ++recId); // create new record
32448 myStore.{@link #insert}(0, r); // insert a new record into the store (also see {@link #add})
32449  * </code></pre>
32450  * <p><u>Writing Data</u></p>
32451  * <p>And <b>new in Ext version 3</b>, use the new {@link Ext.data.DataWriter DataWriter} to create an automated, <a href="http://extjs.com/deploy/dev/examples/writer/writer.html">Writable Store</a>
32452  * along with <a href="http://extjs.com/deploy/dev/examples/restful/restful.html">RESTful features.</a>
32453  * @constructor
32454  * Creates a new Store.
32455  * @param {Object} config A config object containing the objects needed for the Store to access data,
32456  * and read the data into Records.
32457  * @xtype store
32458  */
32459 Ext.data.Store = Ext.extend(Ext.util.Observable, {
32460     /**
32461      * @cfg {String} storeId If passed, the id to use to register with the <b>{@link Ext.StoreMgr StoreMgr}</b>.
32462      * <p><b>Note</b>: if a (deprecated) <tt>{@link #id}</tt> is specified it will supersede the <tt>storeId</tt>
32463      * assignment.</p>
32464      */
32465     /**
32466      * @cfg {String} url If a <tt>{@link #proxy}</tt> is not specified the <tt>url</tt> will be used to
32467      * implicitly configure a {@link Ext.data.HttpProxy HttpProxy} if an <tt>url</tt> is specified.
32468      * Typically this option, or the <code>{@link #data}</code> option will be specified.
32469      */
32470     /**
32471      * @cfg {Boolean/Object} autoLoad If <tt>{@link #data}</tt> is not specified, and if <tt>autoLoad</tt>
32472      * is <tt>true</tt> or an <tt>Object</tt>, this store's {@link #load} method is automatically called
32473      * after creation. If the value of <tt>autoLoad</tt> is an <tt>Object</tt>, this <tt>Object</tt> will
32474      * be passed to the store's {@link #load} method.
32475      */
32476     /**
32477      * @cfg {Ext.data.DataProxy} proxy The {@link Ext.data.DataProxy DataProxy} object which provides
32478      * access to a data object.  See <code>{@link #url}</code>.
32479      */
32480     /**
32481      * @cfg {Array} data An inline data object readable by the <code>{@link #reader}</code>.
32482      * Typically this option, or the <code>{@link #url}</code> option will be specified.
32483      */
32484     /**
32485      * @cfg {Ext.data.DataReader} reader The {@link Ext.data.DataReader Reader} object which processes the
32486      * data object and returns an Array of {@link Ext.data.Record} objects which are cached keyed by their
32487      * <b><tt>{@link Ext.data.Record#id id}</tt></b> property.
32488      */
32489     /**
32490      * @cfg {Ext.data.DataWriter} writer
32491      * <p>The {@link Ext.data.DataWriter Writer} object which processes a record object for being written
32492      * to the server-side database.</p>
32493      * <br><p>When a writer is installed into a Store the {@link #add}, {@link #remove}, and {@link #update}
32494      * events on the store are monitored in order to remotely {@link #createRecords create records},
32495      * {@link #destroyRecord destroy records}, or {@link #updateRecord update records}.</p>
32496      * <br><p>The proxy for this store will relay any {@link #writexception} events to this store.</p>
32497      * <br><p>Sample implementation:
32498      * <pre><code>
32499 var writer = new {@link Ext.data.JsonWriter}({
32500     encode: true,
32501     writeAllFields: true // write all fields, not just those that changed
32502 });
32503
32504 // Typical Store collecting the Proxy, Reader and Writer together.
32505 var store = new Ext.data.Store({
32506     storeId: 'user',
32507     root: 'records',
32508     proxy: proxy,
32509     reader: reader,
32510     writer: writer,     // <-- plug a DataWriter into the store just as you would a Reader
32511     paramsAsHash: true,
32512     autoSave: false    // <-- false to delay executing create, update, destroy requests
32513                         //     until specifically told to do so.
32514 });
32515      * </code></pre></p>
32516      */
32517     writer : undefined,
32518     /**
32519      * @cfg {Object} baseParams
32520      * <p>An object containing properties which are to be sent as parameters
32521      * for <i>every</i> HTTP request.</p>
32522      * <p>Parameters are encoded as standard HTTP parameters using {@link Ext#urlEncode}.</p>
32523      * <p><b>Note</b>: <code>baseParams</code> may be superseded by any <code>params</code>
32524      * specified in a <code>{@link #load}</code> request, see <code>{@link #load}</code>
32525      * for more details.</p>
32526      * This property may be modified after creation using the <code>{@link #setBaseParam}</code>
32527      * method.
32528      * @property
32529      */
32530     /**
32531      * @cfg {Object} sortInfo A config object to specify the sort order in the request of a Store's
32532      * {@link #load} operation.  Note that for local sorting, the <tt>direction</tt> property is
32533      * case-sensitive. See also {@link #remoteSort} and {@link #paramNames}.
32534      * For example:<pre><code>
32535 sortInfo: {
32536     field: 'fieldName',
32537     direction: 'ASC' // or 'DESC' (case sensitive for local sorting)
32538 }
32539 </code></pre>
32540      */
32541     /**
32542      * @cfg {boolean} remoteSort <tt>true</tt> if sorting is to be handled by requesting the <tt>{@link #proxy Proxy}</tt>
32543      * to provide a refreshed version of the data object in sorted order, as opposed to sorting the Record cache
32544      * in place (defaults to <tt>false</tt>).
32545      * <p>If <tt>remoteSort</tt> is <tt>true</tt>, then clicking on a {@link Ext.grid.Column Grid Column}'s
32546      * {@link Ext.grid.Column#header header} causes the current page to be requested from the server appending
32547      * the following two parameters to the <b><tt>{@link #load params}</tt></b>:<div class="mdetail-params"><ul>
32548      * <li><b><tt>sort</tt></b> : String<p class="sub-desc">The <tt>name</tt> (as specified in the Record's
32549      * {@link Ext.data.Field Field definition}) of the field to sort on.</p></li>
32550      * <li><b><tt>dir</tt></b> : String<p class="sub-desc">The direction of the sort, 'ASC' or 'DESC' (case-sensitive).</p></li>
32551      * </ul></div></p>
32552      */
32553     remoteSort : false,
32554
32555     /**
32556      * @cfg {Boolean} autoDestroy <tt>true</tt> to destroy the store when the component the store is bound
32557      * to is destroyed (defaults to <tt>false</tt>).
32558      * <p><b>Note</b>: this should be set to true when using stores that are bound to only 1 component.</p>
32559      */
32560     autoDestroy : false,
32561
32562     /**
32563      * @cfg {Boolean} pruneModifiedRecords <tt>true</tt> to clear all modified record information each time
32564      * the store is loaded or when a record is removed (defaults to <tt>false</tt>). See {@link #getModifiedRecords}
32565      * for the accessor method to retrieve the modified records.
32566      */
32567     pruneModifiedRecords : false,
32568
32569     /**
32570      * Contains the last options object used as the parameter to the {@link #load} method. See {@link #load}
32571      * for the details of what this may contain. This may be useful for accessing any params which were used
32572      * to load the current Record cache.
32573      * @property
32574      */
32575     lastOptions : null,
32576
32577     /**
32578      * @cfg {Boolean} autoSave
32579      * <p>Defaults to <tt>true</tt> causing the store to automatically {@link #save} records to
32580      * the server when a record is modified (ie: becomes 'dirty'). Specify <tt>false</tt> to manually call {@link #save}
32581      * to send all modifiedRecords to the server.</p>
32582      * <br><p><b>Note</b>: each CRUD action will be sent as a separate request.</p>
32583      */
32584     autoSave : true,
32585
32586     /**
32587      * @cfg {Boolean} batch
32588      * <p>Defaults to <tt>true</tt> (unless <code>{@link #restful}:true</code>). Multiple
32589      * requests for each CRUD action (CREATE, READ, UPDATE and DESTROY) will be combined
32590      * and sent as one transaction. Only applies when <code>{@link #autoSave}</code> is set
32591      * to <tt>false</tt>.</p>
32592      * <br><p>If Store is RESTful, the DataProxy is also RESTful, and a unique transaction is
32593      * generated for each record.</p>
32594      */
32595     batch : true,
32596
32597     /**
32598      * @cfg {Boolean} restful
32599      * Defaults to <tt>false</tt>.  Set to <tt>true</tt> to have the Store and the set
32600      * Proxy operate in a RESTful manner. The store will automatically generate GET, POST,
32601      * PUT and DELETE requests to the server. The HTTP method used for any given CRUD
32602      * action is described in {@link Ext.data.Api#restActions}.  For additional information
32603      * see {@link Ext.data.DataProxy#restful}.
32604      * <p><b>Note</b>: if <code>{@link #restful}:true</code> <code>batch</code> will
32605      * internally be set to <tt>false</tt>.</p>
32606      */
32607     restful: false,
32608
32609     /**
32610      * @cfg {Object} paramNames
32611      * <p>An object containing properties which specify the names of the paging and
32612      * sorting parameters passed to remote servers when loading blocks of data. By default, this
32613      * object takes the following form:</p><pre><code>
32614 {
32615     start : 'start',  // The parameter name which specifies the start row
32616     limit : 'limit',  // The parameter name which specifies number of rows to return
32617     sort : 'sort',    // The parameter name which specifies the column to sort on
32618     dir : 'dir'       // The parameter name which specifies the sort direction
32619 }
32620 </code></pre>
32621      * <p>The server must produce the requested data block upon receipt of these parameter names.
32622      * If different parameter names are required, this property can be overriden using a configuration
32623      * property.</p>
32624      * <p>A {@link Ext.PagingToolbar PagingToolbar} bound to this Store uses this property to determine
32625      * the parameter names to use in its {@link #load requests}.
32626      */
32627     paramNames : undefined,
32628
32629     /**
32630      * @cfg {Object} defaultParamNames
32631      * Provides the default values for the {@link #paramNames} property. To globally modify the parameters
32632      * for all stores, this object should be changed on the store prototype.
32633      */
32634     defaultParamNames : {
32635         start : 'start',
32636         limit : 'limit',
32637         sort : 'sort',
32638         dir : 'dir'
32639     },
32640
32641     // private
32642     batchKey : '_ext_batch_',
32643
32644     constructor : function(config){
32645         this.data = new Ext.util.MixedCollection(false);
32646         this.data.getKey = function(o){
32647             return o.id;
32648         };
32649
32650
32651         // temporary removed-records cache
32652         this.removed = [];
32653
32654         if(config && config.data){
32655             this.inlineData = config.data;
32656             delete config.data;
32657         }
32658
32659         Ext.apply(this, config);
32660
32661         /**
32662          * See the <code>{@link #baseParams corresponding configuration option}</code>
32663          * for a description of this property.
32664          * To modify this property see <code>{@link #setBaseParam}</code>.
32665          * @property
32666          */
32667         this.baseParams = Ext.isObject(this.baseParams) ? this.baseParams : {};
32668
32669         this.paramNames = Ext.applyIf(this.paramNames || {}, this.defaultParamNames);
32670
32671         if((this.url || this.api) && !this.proxy){
32672             this.proxy = new Ext.data.HttpProxy({url: this.url, api: this.api});
32673         }
32674         // If Store is RESTful, so too is the DataProxy
32675         if (this.restful === true && this.proxy) {
32676             // When operating RESTfully, a unique transaction is generated for each record.
32677             // TODO might want to allow implemention of faux REST where batch is possible using RESTful routes only.
32678             this.batch = false;
32679             Ext.data.Api.restify(this.proxy);
32680         }
32681
32682         if(this.reader){ // reader passed
32683             if(!this.recordType){
32684                 this.recordType = this.reader.recordType;
32685             }
32686             if(this.reader.onMetaChange){
32687                 this.reader.onMetaChange = this.reader.onMetaChange.createSequence(this.onMetaChange, this);
32688             }
32689             if (this.writer) { // writer passed
32690                 if (this.writer instanceof(Ext.data.DataWriter) === false) {    // <-- config-object instead of instance.
32691                     this.writer = this.buildWriter(this.writer);
32692                 }
32693                 this.writer.meta = this.reader.meta;
32694                 this.pruneModifiedRecords = true;
32695             }
32696         }
32697
32698         /**
32699          * The {@link Ext.data.Record Record} constructor as supplied to (or created by) the
32700          * {@link Ext.data.DataReader Reader}. Read-only.
32701          * <p>If the Reader was constructed by passing in an Array of {@link Ext.data.Field} definition objects,
32702          * instead of a Record constructor, it will implicitly create a Record constructor from that Array (see
32703          * {@link Ext.data.Record}.{@link Ext.data.Record#create create} for additional details).</p>
32704          * <p>This property may be used to create new Records of the type held in this Store, for example:</p><pre><code>
32705     // create the data store
32706     var store = new Ext.data.ArrayStore({
32707         autoDestroy: true,
32708         fields: [
32709            {name: 'company'},
32710            {name: 'price', type: 'float'},
32711            {name: 'change', type: 'float'},
32712            {name: 'pctChange', type: 'float'},
32713            {name: 'lastChange', type: 'date', dateFormat: 'n/j h:ia'}
32714         ]
32715     });
32716     store.loadData(myData);
32717
32718     // create the Grid
32719     var grid = new Ext.grid.EditorGridPanel({
32720         store: store,
32721         colModel: new Ext.grid.ColumnModel({
32722             columns: [
32723                 {id:'company', header: 'Company', width: 160, dataIndex: 'company'},
32724                 {header: 'Price', renderer: 'usMoney', dataIndex: 'price'},
32725                 {header: 'Change', renderer: change, dataIndex: 'change'},
32726                 {header: '% Change', renderer: pctChange, dataIndex: 'pctChange'},
32727                 {header: 'Last Updated', width: 85,
32728                     renderer: Ext.util.Format.dateRenderer('m/d/Y'),
32729                     dataIndex: 'lastChange'}
32730             ],
32731             defaults: {
32732                 sortable: true,
32733                 width: 75
32734             }
32735         }),
32736         autoExpandColumn: 'company', // match the id specified in the column model
32737         height:350,
32738         width:600,
32739         title:'Array Grid',
32740         tbar: [{
32741             text: 'Add Record',
32742             handler : function(){
32743                 var defaultData = {
32744                     change: 0,
32745                     company: 'New Company',
32746                     lastChange: (new Date()).clearTime(),
32747                     pctChange: 0,
32748                     price: 10
32749                 };
32750                 var recId = 3; // provide unique id
32751                 var p = new store.recordType(defaultData, recId); // create new record
32752                 grid.stopEditing();
32753                 store.{@link #insert}(0, p); // insert a new record into the store (also see {@link #add})
32754                 grid.startEditing(0, 0);
32755             }
32756         }]
32757     });
32758          * </code></pre>
32759          * @property recordType
32760          * @type Function
32761          */
32762
32763         if(this.recordType){
32764             /**
32765              * A {@link Ext.util.MixedCollection MixedCollection} containing the defined {@link Ext.data.Field Field}s
32766              * for the {@link Ext.data.Record Records} stored in this Store. Read-only.
32767              * @property fields
32768              * @type Ext.util.MixedCollection
32769              */
32770             this.fields = this.recordType.prototype.fields;
32771         }
32772         this.modified = [];
32773
32774         this.addEvents(
32775             /**
32776              * @event datachanged
32777              * Fires when the data cache has changed in a bulk manner (e.g., it has been sorted, filtered, etc.) and a
32778              * widget that is using this Store as a Record cache should refresh its view.
32779              * @param {Store} this
32780              */
32781             'datachanged',
32782             /**
32783              * @event metachange
32784              * Fires when this store's reader provides new metadata (fields). This is currently only supported for JsonReaders.
32785              * @param {Store} this
32786              * @param {Object} meta The JSON metadata
32787              */
32788             'metachange',
32789             /**
32790              * @event add
32791              * Fires when Records have been {@link #add}ed to the Store
32792              * @param {Store} this
32793              * @param {Ext.data.Record[]} records The array of Records added
32794              * @param {Number} index The index at which the record(s) were added
32795              */
32796             'add',
32797             /**
32798              * @event remove
32799              * Fires when a Record has been {@link #remove}d from the Store
32800              * @param {Store} this
32801              * @param {Ext.data.Record} record The Record that was removed
32802              * @param {Number} index The index at which the record was removed
32803              */
32804             'remove',
32805             /**
32806              * @event update
32807              * Fires when a Record has been updated
32808              * @param {Store} this
32809              * @param {Ext.data.Record} record The Record that was updated
32810              * @param {String} operation The update operation being performed.  Value may be one of:
32811              * <pre><code>
32812      Ext.data.Record.EDIT
32813      Ext.data.Record.REJECT
32814      Ext.data.Record.COMMIT
32815              * </code></pre>
32816              */
32817             'update',
32818             /**
32819              * @event clear
32820              * Fires when the data cache has been cleared.
32821              * @param {Store} this
32822              * @param {Record[]} The records that were cleared.
32823              */
32824             'clear',
32825             /**
32826              * @event exception
32827              * <p>Fires if an exception occurs in the Proxy during a remote request.
32828              * This event is relayed through the corresponding {@link Ext.data.DataProxy}.
32829              * See {@link Ext.data.DataProxy}.{@link Ext.data.DataProxy#exception exception}
32830              * for additional details.
32831              * @param {misc} misc See {@link Ext.data.DataProxy}.{@link Ext.data.DataProxy#exception exception}
32832              * for description.
32833              */
32834             'exception',
32835             /**
32836              * @event beforeload
32837              * Fires before a request is made for a new data object.  If the beforeload handler returns
32838              * <tt>false</tt> the {@link #load} action will be canceled.
32839              * @param {Store} this
32840              * @param {Object} options The loading options that were specified (see {@link #load} for details)
32841              */
32842             'beforeload',
32843             /**
32844              * @event load
32845              * Fires after a new set of Records has been loaded.
32846              * @param {Store} this
32847              * @param {Ext.data.Record[]} records The Records that were loaded
32848              * @param {Object} options The loading options that were specified (see {@link #load} for details)
32849              */
32850             'load',
32851             /**
32852              * @event loadexception
32853              * <p>This event is <b>deprecated</b> in favor of the catch-all <b><code>{@link #exception}</code></b>
32854              * event instead.</p>
32855              * <p>This event is relayed through the corresponding {@link Ext.data.DataProxy}.
32856              * See {@link Ext.data.DataProxy}.{@link Ext.data.DataProxy#loadexception loadexception}
32857              * for additional details.
32858              * @param {misc} misc See {@link Ext.data.DataProxy}.{@link Ext.data.DataProxy#loadexception loadexception}
32859              * for description.
32860              */
32861             'loadexception',
32862             /**
32863              * @event beforewrite
32864              * @param {Ext.data.Store} store
32865              * @param {String} action [Ext.data.Api.actions.create|update|destroy]
32866              * @param {Record/Array[Record]} rs
32867              * @param {Object} options The loading options that were specified. Edit <code>options.params</code> to add Http parameters to the request.  (see {@link #save} for details)
32868              * @param {Object} arg The callback's arg object passed to the {@link #request} function
32869              */
32870             'beforewrite',
32871             /**
32872              * @event write
32873              * Fires if the server returns 200 after an Ext.data.Api.actions CRUD action.
32874              * Success of the action is determined in the <code>result['successProperty']</code>property (<b>NOTE</b> for RESTful stores,
32875              * a simple 20x response is sufficient for the actions "destroy" and "update".  The "create" action should should return 200 along with a database pk).
32876              * @param {Ext.data.Store} store
32877              * @param {String} action [Ext.data.Api.actions.create|update|destroy]
32878              * @param {Object} result The 'data' picked-out out of the response for convenience.
32879              * @param {Ext.Direct.Transaction} res
32880              * @param {Record/Record[]} rs Store's records, the subject(s) of the write-action
32881              */
32882             'write',
32883             /**
32884              * @event beforesave
32885              * Fires before a save action is called. A save encompasses destroying records, updating records and creating records.
32886              * @param {Ext.data.Store} store
32887              * @param {Object} data An object containing the data that is to be saved. The object will contain a key for each appropriate action,
32888              * with an array of records for each action.
32889              */
32890             'beforesave',
32891             /**
32892              * @event save
32893              * Fires after a save is completed. A save encompasses destroying records, updating records and creating records.
32894              * @param {Ext.data.Store} store
32895              * @param {Number} batch The identifier for the batch that was saved.
32896              * @param {Object} data An object containing the data that is to be saved. The object will contain a key for each appropriate action,
32897              * with an array of records for each action.
32898              */
32899             'save'
32900
32901         );
32902
32903         if(this.proxy){
32904             // TODO remove deprecated loadexception with ext-3.0.1
32905             this.relayEvents(this.proxy,  ['loadexception', 'exception']);
32906         }
32907         // With a writer set for the Store, we want to listen to add/remove events to remotely create/destroy records.
32908         if (this.writer) {
32909             this.on({
32910                 scope: this,
32911                 add: this.createRecords,
32912                 remove: this.destroyRecord,
32913                 update: this.updateRecord,
32914                 clear: this.onClear
32915             });
32916         }
32917
32918         this.sortToggle = {};
32919         if(this.sortField){
32920             this.setDefaultSort(this.sortField, this.sortDir);
32921         }else if(this.sortInfo){
32922             this.setDefaultSort(this.sortInfo.field, this.sortInfo.direction);
32923         }
32924
32925         Ext.data.Store.superclass.constructor.call(this);
32926
32927         if(this.id){
32928             this.storeId = this.id;
32929             delete this.id;
32930         }
32931         if(this.storeId){
32932             Ext.StoreMgr.register(this);
32933         }
32934         if(this.inlineData){
32935             this.loadData(this.inlineData);
32936             delete this.inlineData;
32937         }else if(this.autoLoad){
32938             this.load.defer(10, this, [
32939                 typeof this.autoLoad == 'object' ?
32940                     this.autoLoad : undefined]);
32941         }
32942         // used internally to uniquely identify a batch
32943         this.batchCounter = 0;
32944         this.batches = {};
32945     },
32946
32947     /**
32948      * builds a DataWriter instance when Store constructor is provided with a writer config-object instead of an instace.
32949      * @param {Object} config Writer configuration
32950      * @return {Ext.data.DataWriter}
32951      * @private
32952      */
32953     buildWriter : function(config) {
32954         var klass = undefined,
32955             type = (config.format || 'json').toLowerCase();
32956         switch (type) {
32957             case 'json':
32958                 klass = Ext.data.JsonWriter;
32959                 break;
32960             case 'xml':
32961                 klass = Ext.data.XmlWriter;
32962                 break;
32963             default:
32964                 klass = Ext.data.JsonWriter;
32965         }
32966         return new klass(config);
32967     },
32968
32969     /**
32970      * Destroys the store.
32971      */
32972     destroy : function(){
32973         if(!this.isDestroyed){
32974             if(this.storeId){
32975                 Ext.StoreMgr.unregister(this);
32976             }
32977             this.clearData();
32978             this.data = null;
32979             Ext.destroy(this.proxy);
32980             this.reader = this.writer = null;
32981             this.purgeListeners();
32982             this.isDestroyed = true;
32983         }
32984     },
32985
32986     /**
32987      * Add Records to the Store and fires the {@link #add} event.  To add Records
32988      * to the store from a remote source use <code>{@link #load}({add:true})</code>.
32989      * See also <code>{@link #recordType}</code> and <code>{@link #insert}</code>.
32990      * @param {Ext.data.Record[]} records An Array of Ext.data.Record objects
32991      * to add to the cache. See {@link #recordType}.
32992      */
32993     add : function(records){
32994         records = [].concat(records);
32995         if(records.length < 1){
32996             return;
32997         }
32998         for(var i = 0, len = records.length; i < len; i++){
32999             records[i].join(this);
33000         }
33001         var index = this.data.length;
33002         this.data.addAll(records);
33003         if(this.snapshot){
33004             this.snapshot.addAll(records);
33005         }
33006         this.fireEvent('add', this, records, index);
33007     },
33008
33009     /**
33010      * (Local sort only) Inserts the passed Record into the Store at the index where it
33011      * should go based on the current sort information.
33012      * @param {Ext.data.Record} record
33013      */
33014     addSorted : function(record){
33015         var index = this.findInsertIndex(record);
33016         this.insert(index, record);
33017     },
33018
33019     /**
33020      * Remove Records from the Store and fires the {@link #remove} event.
33021      * @param {Ext.data.Record/Ext.data.Record[]} record The record object or array of records to remove from the cache.
33022      */
33023     remove : function(record){
33024         if(Ext.isArray(record)){
33025             Ext.each(record, function(r){
33026                 this.remove(r);
33027             }, this);
33028         }
33029         var index = this.data.indexOf(record);
33030         if(index > -1){
33031             record.join(null);
33032             this.data.removeAt(index);
33033         }
33034         if(this.pruneModifiedRecords){
33035             this.modified.remove(record);
33036         }
33037         if(this.snapshot){
33038             this.snapshot.remove(record);
33039         }
33040         if(index > -1){
33041             this.fireEvent('remove', this, record, index);
33042         }
33043     },
33044
33045     /**
33046      * Remove a Record from the Store at the specified index. Fires the {@link #remove} event.
33047      * @param {Number} index The index of the record to remove.
33048      */
33049     removeAt : function(index){
33050         this.remove(this.getAt(index));
33051     },
33052
33053     /**
33054      * Remove all Records from the Store and fires the {@link #clear} event.
33055      * @param {Boolean} silent [false] Defaults to <tt>false</tt>.  Set <tt>true</tt> to not fire clear event.
33056      */
33057     removeAll : function(silent){
33058         var items = [];
33059         this.each(function(rec){
33060             items.push(rec);
33061         });
33062         this.clearData();
33063         if(this.snapshot){
33064             this.snapshot.clear();
33065         }
33066         if(this.pruneModifiedRecords){
33067             this.modified = [];
33068         }
33069         if (silent !== true) {  // <-- prevents write-actions when we just want to clear a store.
33070             this.fireEvent('clear', this, items);
33071         }
33072     },
33073
33074     // private
33075     onClear: function(store, records){
33076         Ext.each(records, function(rec, index){
33077             this.destroyRecord(this, rec, index);
33078         }, this);
33079     },
33080
33081     /**
33082      * Inserts Records into the Store at the given index and fires the {@link #add} event.
33083      * See also <code>{@link #add}</code> and <code>{@link #addSorted}</code>.
33084      * @param {Number} index The start index at which to insert the passed Records.
33085      * @param {Ext.data.Record[]} records An Array of Ext.data.Record objects to add to the cache.
33086      */
33087     insert : function(index, records){
33088         records = [].concat(records);
33089         for(var i = 0, len = records.length; i < len; i++){
33090             this.data.insert(index, records[i]);
33091             records[i].join(this);
33092         }
33093         if(this.snapshot){
33094             this.snapshot.addAll(records);
33095         }
33096         this.fireEvent('add', this, records, index);
33097     },
33098
33099     /**
33100      * Get the index within the cache of the passed Record.
33101      * @param {Ext.data.Record} record The Ext.data.Record object to find.
33102      * @return {Number} The index of the passed Record. Returns -1 if not found.
33103      */
33104     indexOf : function(record){
33105         return this.data.indexOf(record);
33106     },
33107
33108     /**
33109      * Get the index within the cache of the Record with the passed id.
33110      * @param {String} id The id of the Record to find.
33111      * @return {Number} The index of the Record. Returns -1 if not found.
33112      */
33113     indexOfId : function(id){
33114         return this.data.indexOfKey(id);
33115     },
33116
33117     /**
33118      * Get the Record with the specified id.
33119      * @param {String} id The id of the Record to find.
33120      * @return {Ext.data.Record} The Record with the passed id. Returns undefined if not found.
33121      */
33122     getById : function(id){
33123         return (this.snapshot || this.data).key(id);
33124     },
33125
33126     /**
33127      * Get the Record at the specified index.
33128      * @param {Number} index The index of the Record to find.
33129      * @return {Ext.data.Record} The Record at the passed index. Returns undefined if not found.
33130      */
33131     getAt : function(index){
33132         return this.data.itemAt(index);
33133     },
33134
33135     /**
33136      * Returns a range of Records between specified indices.
33137      * @param {Number} startIndex (optional) The starting index (defaults to 0)
33138      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
33139      * @return {Ext.data.Record[]} An array of Records
33140      */
33141     getRange : function(start, end){
33142         return this.data.getRange(start, end);
33143     },
33144
33145     // private
33146     storeOptions : function(o){
33147         o = Ext.apply({}, o);
33148         delete o.callback;
33149         delete o.scope;
33150         this.lastOptions = o;
33151     },
33152
33153     // private
33154     clearData: function(){
33155         this.data.each(function(rec) {
33156             rec.join(null);
33157         });
33158         this.data.clear();
33159     },
33160
33161     /**
33162      * <p>Loads the Record cache from the configured <tt>{@link #proxy}</tt> using the configured <tt>{@link #reader}</tt>.</p>
33163      * <br><p>Notes:</p><div class="mdetail-params"><ul>
33164      * <li><b><u>Important</u></b>: loading is asynchronous! This call will return before the new data has been
33165      * loaded. To perform any post-processing where information from the load call is required, specify
33166      * the <tt>callback</tt> function to be called, or use a {@link Ext.util.Observable#listeners a 'load' event handler}.</li>
33167      * <li>If using {@link Ext.PagingToolbar remote paging}, the first load call must specify the <tt>start</tt> and <tt>limit</tt>
33168      * properties in the <code>options.params</code> property to establish the initial position within the
33169      * dataset, and the number of Records to cache on each read from the Proxy.</li>
33170      * <li>If using {@link #remoteSort remote sorting}, the configured <code>{@link #sortInfo}</code>
33171      * will be automatically included with the posted parameters according to the specified
33172      * <code>{@link #paramNames}</code>.</li>
33173      * </ul></div>
33174      * @param {Object} options An object containing properties which control loading options:<ul>
33175      * <li><b><tt>params</tt></b> :Object<div class="sub-desc"><p>An object containing properties to pass as HTTP
33176      * parameters to a remote data source. <b>Note</b>: <code>params</code> will override any
33177      * <code>{@link #baseParams}</code> of the same name.</p>
33178      * <p>Parameters are encoded as standard HTTP parameters using {@link Ext#urlEncode}.</p></div></li>
33179      * <li><b>callback</b> : Function<div class="sub-desc"><p>A function to be called after the Records
33180      * have been loaded. The callback is called after the load event is fired, and is passed the following arguments:<ul>
33181      * <li>r : Ext.data.Record[] An Array of Records loaded.</li>
33182      * <li>options : Options object from the load call.</li>
33183      * <li>success : Boolean success indicator.</li></ul></p></div></li>
33184      * <li><b>scope</b> : Object<div class="sub-desc"><p>Scope with which to call the callback (defaults
33185      * to the Store object)</p></div></li>
33186      * <li><b>add</b> : Boolean<div class="sub-desc"><p>Indicator to append loaded records rather than
33187      * replace the current cache.  <b>Note</b>: see note for <tt>{@link #loadData}</tt></p></div></li>
33188      * </ul>
33189      * @return {Boolean} If the <i>developer</i> provided <tt>{@link #beforeload}</tt> event handler returns
33190      * <tt>false</tt>, the load call will abort and will return <tt>false</tt>; otherwise will return <tt>true</tt>.
33191      */
33192     load : function(options) {
33193         options = options || {};
33194         this.storeOptions(options);
33195         if(this.sortInfo && this.remoteSort){
33196             var pn = this.paramNames;
33197             options.params = options.params || {};
33198             options.params[pn.sort] = this.sortInfo.field;
33199             options.params[pn.dir] = this.sortInfo.direction;
33200         }
33201         try {
33202             return this.execute('read', null, options); // <-- null represents rs.  No rs for load actions.
33203         } catch(e) {
33204             this.handleException(e);
33205             return false;
33206         }
33207     },
33208
33209     /**
33210      * updateRecord  Should not be used directly.  This method will be called automatically if a Writer is set.
33211      * Listens to 'update' event.
33212      * @param {Object} store
33213      * @param {Object} record
33214      * @param {Object} action
33215      * @private
33216      */
33217     updateRecord : function(store, record, action) {
33218         if (action == Ext.data.Record.EDIT && this.autoSave === true && (!record.phantom || (record.phantom && record.isValid()))) {
33219             this.save();
33220         }
33221     },
33222
33223     /**
33224      * Should not be used directly.  Store#add will call this automatically if a Writer is set
33225      * @param {Object} store
33226      * @param {Object} rs
33227      * @param {Object} index
33228      * @private
33229      */
33230     createRecords : function(store, rs, index) {
33231         for (var i = 0, len = rs.length; i < len; i++) {
33232             if (rs[i].phantom && rs[i].isValid()) {
33233                 rs[i].markDirty();  // <-- Mark new records dirty
33234                 this.modified.push(rs[i]);  // <-- add to modified
33235             }
33236         }
33237         if (this.autoSave === true) {
33238             this.save();
33239         }
33240     },
33241
33242     /**
33243      * Destroys a record or records.  Should not be used directly.  It's called by Store#remove if a Writer is set.
33244      * @param {Store} this
33245      * @param {Ext.data.Record/Ext.data.Record[]}
33246      * @param {Number} index
33247      * @private
33248      */
33249     destroyRecord : function(store, record, index) {
33250         if (this.modified.indexOf(record) != -1) {  // <-- handled already if @cfg pruneModifiedRecords == true
33251             this.modified.remove(record);
33252         }
33253         if (!record.phantom) {
33254             this.removed.push(record);
33255
33256             // since the record has already been removed from the store but the server request has not yet been executed,
33257             // must keep track of the last known index this record existed.  If a server error occurs, the record can be
33258             // put back into the store.  @see Store#createCallback where the record is returned when response status === false
33259             record.lastIndex = index;
33260
33261             if (this.autoSave === true) {
33262                 this.save();
33263             }
33264         }
33265     },
33266
33267     /**
33268      * This method should generally not be used directly.  This method is called internally
33269      * by {@link #load}, or if a Writer is set will be called automatically when {@link #add},
33270      * {@link #remove}, or {@link #update} events fire.
33271      * @param {String} action Action name ('read', 'create', 'update', or 'destroy')
33272      * @param {Record/Record[]} rs
33273      * @param {Object} options
33274      * @throws Error
33275      * @private
33276      */
33277     execute : function(action, rs, options, /* private */ batch) {
33278         // blow up if action not Ext.data.CREATE, READ, UPDATE, DESTROY
33279         if (!Ext.data.Api.isAction(action)) {
33280             throw new Ext.data.Api.Error('execute', action);
33281         }
33282         // make sure options has a fresh, new params hash
33283         options = Ext.applyIf(options||{}, {
33284             params: {}
33285         });
33286         if(batch !== undefined){
33287             this.addToBatch(batch);
33288         }
33289         // have to separate before-events since load has a different signature than create,destroy and save events since load does not
33290         // include the rs (record resultset) parameter.  Capture return values from the beforeaction into doRequest flag.
33291         var doRequest = true;
33292
33293         if (action === 'read') {
33294             doRequest = this.fireEvent('beforeload', this, options);
33295             Ext.applyIf(options.params, this.baseParams);
33296         }
33297         else {
33298             // if Writer is configured as listful, force single-record rs to be [{}] instead of {}
33299             // TODO Move listful rendering into DataWriter where the @cfg is defined.  Should be easy now.
33300             if (this.writer.listful === true && this.restful !== true) {
33301                 rs = (Ext.isArray(rs)) ? rs : [rs];
33302             }
33303             // if rs has just a single record, shift it off so that Writer writes data as '{}' rather than '[{}]'
33304             else if (Ext.isArray(rs) && rs.length == 1) {
33305                 rs = rs.shift();
33306             }
33307             // Write the action to options.params
33308             if ((doRequest = this.fireEvent('beforewrite', this, action, rs, options)) !== false) {
33309                 this.writer.apply(options.params, this.baseParams, action, rs);
33310             }
33311         }
33312         if (doRequest !== false) {
33313             // Send request to proxy.
33314             if (this.writer && this.proxy.url && !this.proxy.restful && !Ext.data.Api.hasUniqueUrl(this.proxy, action)) {
33315                 options.params.xaction = action;    // <-- really old, probaby unecessary.
33316             }
33317             // Note:  Up until this point we've been dealing with 'action' as a key from Ext.data.Api.actions.
33318             // We'll flip it now and send the value into DataProxy#request, since it's the value which maps to
33319             // the user's configured DataProxy#api
33320             // TODO Refactor all Proxies to accept an instance of Ext.data.Request (not yet defined) instead of this looooooong list
33321             // of params.  This method is an artifact from Ext2.
33322             this.proxy.request(Ext.data.Api.actions[action], rs, options.params, this.reader, this.createCallback(action, rs, batch), this, options);
33323         }
33324         return doRequest;
33325     },
33326
33327     /**
33328      * Saves all pending changes to the store.  If the commensurate Ext.data.Api.actions action is not configured, then
33329      * the configured <code>{@link #url}</code> will be used.
33330      * <pre>
33331      * change            url
33332      * ---------------   --------------------
33333      * removed records   Ext.data.Api.actions.destroy
33334      * phantom records   Ext.data.Api.actions.create
33335      * {@link #getModifiedRecords modified records}  Ext.data.Api.actions.update
33336      * </pre>
33337      * @TODO:  Create extensions of Error class and send associated Record with thrown exceptions.
33338      * e.g.:  Ext.data.DataReader.Error or Ext.data.Error or Ext.data.DataProxy.Error, etc.
33339      * @return {Number} batch Returns a number to uniquely identify the "batch" of saves occurring. -1 will be returned
33340      * if there are no items to save or the save was cancelled.
33341      */
33342     save : function() {
33343         if (!this.writer) {
33344             throw new Ext.data.Store.Error('writer-undefined');
33345         }
33346
33347         var queue = [],
33348             len,
33349             trans,
33350             batch,
33351             data = {};
33352         // DESTROY:  First check for removed records.  Records in this.removed are guaranteed non-phantoms.  @see Store#remove
33353         if(this.removed.length){
33354             queue.push(['destroy', this.removed]);
33355         }
33356
33357         // Check for modified records. Use a copy so Store#rejectChanges will work if server returns error.
33358         var rs = [].concat(this.getModifiedRecords());
33359         if(rs.length){
33360             // CREATE:  Next check for phantoms within rs.  splice-off and execute create.
33361             var phantoms = [];
33362             for(var i = rs.length-1; i >= 0; i--){
33363                 if(rs[i].phantom === true){
33364                     var rec = rs.splice(i, 1).shift();
33365                     if(rec.isValid()){
33366                         phantoms.push(rec);
33367                     }
33368                 }else if(!rs[i].isValid()){ // <-- while we're here, splice-off any !isValid real records
33369                     rs.splice(i,1);
33370                 }
33371             }
33372             // If we have valid phantoms, create them...
33373             if(phantoms.length){
33374                 queue.push(['create', phantoms]);
33375             }
33376
33377             // UPDATE:  And finally, if we're still here after splicing-off phantoms and !isValid real records, update the rest...
33378             if(rs.length){
33379                 queue.push(['update', rs]);
33380             }
33381         }
33382         len = queue.length;
33383         if(len){
33384             batch = ++this.batchCounter;
33385             for(var i = 0; i < len; ++i){
33386                 trans = queue[i];
33387                 data[trans[0]] = trans[1];
33388             }
33389             if(this.fireEvent('beforesave', this, data) !== false){
33390                 for(var i = 0; i < len; ++i){
33391                     trans = queue[i];
33392                     this.doTransaction(trans[0], trans[1], batch);
33393                 }
33394                 return batch;
33395             }
33396         }
33397         return -1;
33398     },
33399
33400     // private.  Simply wraps call to Store#execute in try/catch.  Defers to Store#handleException on error.  Loops if batch: false
33401     doTransaction : function(action, rs, batch) {
33402         function transaction(records) {
33403             try{
33404                 this.execute(action, records, undefined, batch);
33405             }catch (e){
33406                 this.handleException(e);
33407             }
33408         }
33409         if(this.batch === false){
33410             for(var i = 0, len = rs.length; i < len; i++){
33411                 transaction.call(this, rs[i]);
33412             }
33413         }else{
33414             transaction.call(this, rs);
33415         }
33416     },
33417
33418     // private
33419     addToBatch : function(batch){
33420         var b = this.batches,
33421             key = this.batchKey + batch,
33422             o = b[key];
33423
33424         if(!o){
33425             b[key] = o = {
33426                 id: batch,
33427                 count: 0,
33428                 data: {}
33429             }
33430         }
33431         ++o.count;
33432     },
33433
33434     removeFromBatch : function(batch, action, data){
33435         var b = this.batches,
33436             key = this.batchKey + batch,
33437             o = b[key],
33438             data,
33439             arr;
33440
33441
33442         if(o){
33443             arr = o.data[action] || [];
33444             o.data[action] = arr.concat(data);
33445             if(o.count === 1){
33446                 data = o.data;
33447                 delete b[key];
33448                 this.fireEvent('save', this, batch, data);
33449             }else{
33450                 --o.count;
33451             }
33452         }
33453     },
33454
33455     // @private callback-handler for remote CRUD actions
33456     // Do not override -- override loadRecords, onCreateRecords, onDestroyRecords and onUpdateRecords instead.
33457     createCallback : function(action, rs, batch) {
33458         var actions = Ext.data.Api.actions;
33459         return (action == 'read') ? this.loadRecords : function(data, response, success) {
33460             // calls: onCreateRecords | onUpdateRecords | onDestroyRecords
33461             this['on' + Ext.util.Format.capitalize(action) + 'Records'](success, rs, [].concat(data));
33462             // If success === false here, exception will have been called in DataProxy
33463             if (success === true) {
33464                 this.fireEvent('write', this, action, data, response, rs);
33465             }
33466             this.removeFromBatch(batch, action, data);
33467         };
33468     },
33469
33470     // Clears records from modified array after an exception event.
33471     // NOTE:  records are left marked dirty.  Do we want to commit them even though they were not updated/realized?
33472     // TODO remove this method?
33473     clearModified : function(rs) {
33474         if (Ext.isArray(rs)) {
33475             for (var n=rs.length-1;n>=0;n--) {
33476                 this.modified.splice(this.modified.indexOf(rs[n]), 1);
33477             }
33478         } else {
33479             this.modified.splice(this.modified.indexOf(rs), 1);
33480         }
33481     },
33482
33483     // remap record ids in MixedCollection after records have been realized.  @see Store#onCreateRecords, @see DataReader#realize
33484     reMap : function(record) {
33485         if (Ext.isArray(record)) {
33486             for (var i = 0, len = record.length; i < len; i++) {
33487                 this.reMap(record[i]);
33488             }
33489         } else {
33490             delete this.data.map[record._phid];
33491             this.data.map[record.id] = record;
33492             var index = this.data.keys.indexOf(record._phid);
33493             this.data.keys.splice(index, 1, record.id);
33494             delete record._phid;
33495         }
33496     },
33497
33498     // @protected onCreateRecord proxy callback for create action
33499     onCreateRecords : function(success, rs, data) {
33500         if (success === true) {
33501             try {
33502                 this.reader.realize(rs, data);
33503                 this.reMap(rs);
33504             }
33505             catch (e) {
33506                 this.handleException(e);
33507                 if (Ext.isArray(rs)) {
33508                     // Recurse to run back into the try {}.  DataReader#realize splices-off the rs until empty.
33509                     this.onCreateRecords(success, rs, data);
33510                 }
33511             }
33512         }
33513     },
33514
33515     // @protected, onUpdateRecords proxy callback for update action
33516     onUpdateRecords : function(success, rs, data) {
33517         if (success === true) {
33518             try {
33519                 this.reader.update(rs, data);
33520             } catch (e) {
33521                 this.handleException(e);
33522                 if (Ext.isArray(rs)) {
33523                     // Recurse to run back into the try {}.  DataReader#update splices-off the rs until empty.
33524                     this.onUpdateRecords(success, rs, data);
33525                 }
33526             }
33527         }
33528     },
33529
33530     // @protected onDestroyRecords proxy callback for destroy action
33531     onDestroyRecords : function(success, rs, data) {
33532         // splice each rec out of this.removed
33533         rs = (rs instanceof Ext.data.Record) ? [rs] : [].concat(rs);
33534         for (var i=0,len=rs.length;i<len;i++) {
33535             this.removed.splice(this.removed.indexOf(rs[i]), 1);
33536         }
33537         if (success === false) {
33538             // put records back into store if remote destroy fails.
33539             // @TODO: Might want to let developer decide.
33540             for (i=rs.length-1;i>=0;i--) {
33541                 this.insert(rs[i].lastIndex, rs[i]);    // <-- lastIndex set in Store#destroyRecord
33542             }
33543         }
33544     },
33545
33546     // protected handleException.  Possibly temporary until Ext framework has an exception-handler.
33547     handleException : function(e) {
33548         // @see core/Error.js
33549         Ext.handleError(e);
33550     },
33551
33552     /**
33553      * <p>Reloads the Record cache from the configured Proxy using the configured
33554      * {@link Ext.data.Reader Reader} and the options from the last load operation
33555      * performed.</p>
33556      * <p><b>Note</b>: see the Important note in {@link #load}.</p>
33557      * @param {Object} options <p>(optional) An <tt>Object</tt> containing
33558      * {@link #load loading options} which may override the {@link #lastOptions options}
33559      * used in the last {@link #load} operation. See {@link #load} for details
33560      * (defaults to <tt>null</tt>, in which case the {@link #lastOptions} are
33561      * used).</p>
33562      * <br><p>To add new params to the existing params:</p><pre><code>
33563 lastOptions = myStore.lastOptions;
33564 Ext.apply(lastOptions.params, {
33565     myNewParam: true
33566 });
33567 myStore.reload(lastOptions);
33568      * </code></pre>
33569      */
33570     reload : function(options){
33571         this.load(Ext.applyIf(options||{}, this.lastOptions));
33572     },
33573
33574     // private
33575     // Called as a callback by the Reader during a load operation.
33576     loadRecords : function(o, options, success){
33577         if (this.isDestroyed === true) {
33578             return;
33579         }
33580         if(!o || success === false){
33581             if(success !== false){
33582                 this.fireEvent('load', this, [], options);
33583             }
33584             if(options.callback){
33585                 options.callback.call(options.scope || this, [], options, false, o);
33586             }
33587             return;
33588         }
33589         var r = o.records, t = o.totalRecords || r.length;
33590         if(!options || options.add !== true){
33591             if(this.pruneModifiedRecords){
33592                 this.modified = [];
33593             }
33594             for(var i = 0, len = r.length; i < len; i++){
33595                 r[i].join(this);
33596             }
33597             if(this.snapshot){
33598                 this.data = this.snapshot;
33599                 delete this.snapshot;
33600             }
33601             this.clearData();
33602             this.data.addAll(r);
33603             this.totalLength = t;
33604             this.applySort();
33605             this.fireEvent('datachanged', this);
33606         }else{
33607             this.totalLength = Math.max(t, this.data.length+r.length);
33608             this.add(r);
33609         }
33610         this.fireEvent('load', this, r, options);
33611         if(options.callback){
33612             options.callback.call(options.scope || this, r, options, true);
33613         }
33614     },
33615
33616     /**
33617      * Loads data from a passed data block and fires the {@link #load} event. A {@link Ext.data.Reader Reader}
33618      * which understands the format of the data must have been configured in the constructor.
33619      * @param {Object} data The data block from which to read the Records.  The format of the data expected
33620      * is dependent on the type of {@link Ext.data.Reader Reader} that is configured and should correspond to
33621      * that {@link Ext.data.Reader Reader}'s <tt>{@link Ext.data.Reader#readRecords}</tt> parameter.
33622      * @param {Boolean} append (Optional) <tt>true</tt> to append the new Records rather the default to replace
33623      * the existing cache.
33624      * <b>Note</b>: that Records in a Store are keyed by their {@link Ext.data.Record#id id}, so added Records
33625      * with ids which are already present in the Store will <i>replace</i> existing Records. Only Records with
33626      * new, unique ids will be added.
33627      */
33628     loadData : function(o, append){
33629         var r = this.reader.readRecords(o);
33630         this.loadRecords(r, {add: append}, true);
33631     },
33632
33633     /**
33634      * Gets the number of cached records.
33635      * <p>If using paging, this may not be the total size of the dataset. If the data object
33636      * used by the Reader contains the dataset size, then the {@link #getTotalCount} function returns
33637      * the dataset size.  <b>Note</b>: see the Important note in {@link #load}.</p>
33638      * @return {Number} The number of Records in the Store's cache.
33639      */
33640     getCount : function(){
33641         return this.data.length || 0;
33642     },
33643
33644     /**
33645      * Gets the total number of records in the dataset as returned by the server.
33646      * <p>If using paging, for this to be accurate, the data object used by the {@link #reader Reader}
33647      * must contain the dataset size. For remote data sources, the value for this property
33648      * (<tt>totalProperty</tt> for {@link Ext.data.JsonReader JsonReader},
33649      * <tt>totalRecords</tt> for {@link Ext.data.XmlReader XmlReader}) shall be returned by a query on the server.
33650      * <b>Note</b>: see the Important note in {@link #load}.</p>
33651      * @return {Number} The number of Records as specified in the data object passed to the Reader
33652      * by the Proxy.
33653      * <p><b>Note</b>: this value is not updated when changing the contents of the Store locally.</p>
33654      */
33655     getTotalCount : function(){
33656         return this.totalLength || 0;
33657     },
33658
33659     /**
33660      * Returns an object describing the current sort state of this Store.
33661      * @return {Object} The sort state of the Store. An object with two properties:<ul>
33662      * <li><b>field : String<p class="sub-desc">The name of the field by which the Records are sorted.</p></li>
33663      * <li><b>direction : String<p class="sub-desc">The sort order, 'ASC' or 'DESC' (case-sensitive).</p></li>
33664      * </ul>
33665      * See <tt>{@link #sortInfo}</tt> for additional details.
33666      */
33667     getSortState : function(){
33668         return this.sortInfo;
33669     },
33670
33671     // private
33672     applySort : function(){
33673         if(this.sortInfo && !this.remoteSort){
33674             var s = this.sortInfo, f = s.field;
33675             this.sortData(f, s.direction);
33676         }
33677     },
33678
33679     // private
33680     sortData : function(f, direction){
33681         direction = direction || 'ASC';
33682         var st = this.fields.get(f).sortType;
33683         var fn = function(r1, r2){
33684             var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
33685             return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
33686         };
33687         this.data.sort(direction, fn);
33688         if(this.snapshot && this.snapshot != this.data){
33689             this.snapshot.sort(direction, fn);
33690         }
33691     },
33692
33693     /**
33694      * Sets the default sort column and order to be used by the next {@link #load} operation.
33695      * @param {String} fieldName The name of the field to sort by.
33696      * @param {String} dir (optional) The sort order, 'ASC' or 'DESC' (case-sensitive, defaults to <tt>'ASC'</tt>)
33697      */
33698     setDefaultSort : function(field, dir){
33699         dir = dir ? dir.toUpperCase() : 'ASC';
33700         this.sortInfo = {field: field, direction: dir};
33701         this.sortToggle[field] = dir;
33702     },
33703
33704     /**
33705      * Sort the Records.
33706      * If remote sorting is used, the sort is performed on the server, and the cache is reloaded. If local
33707      * sorting is used, the cache is sorted internally. See also {@link #remoteSort} and {@link #paramNames}.
33708      * @param {String} fieldName The name of the field to sort by.
33709      * @param {String} dir (optional) The sort order, 'ASC' or 'DESC' (case-sensitive, defaults to <tt>'ASC'</tt>)
33710      */
33711     sort : function(fieldName, dir){
33712         var f = this.fields.get(fieldName);
33713         if(!f){
33714             return false;
33715         }
33716         if(!dir){
33717             if(this.sortInfo && this.sortInfo.field == f.name){ // toggle sort dir
33718                 dir = (this.sortToggle[f.name] || 'ASC').toggle('ASC', 'DESC');
33719             }else{
33720                 dir = f.sortDir;
33721             }
33722         }
33723         var st = (this.sortToggle) ? this.sortToggle[f.name] : null;
33724         var si = (this.sortInfo) ? this.sortInfo : null;
33725
33726         this.sortToggle[f.name] = dir;
33727         this.sortInfo = {field: f.name, direction: dir};
33728         if(!this.remoteSort){
33729             this.applySort();
33730             this.fireEvent('datachanged', this);
33731         }else{
33732             if (!this.load(this.lastOptions)) {
33733                 if (st) {
33734                     this.sortToggle[f.name] = st;
33735                 }
33736                 if (si) {
33737                     this.sortInfo = si;
33738                 }
33739             }
33740         }
33741     },
33742
33743     /**
33744      * Calls the specified function for each of the {@link Ext.data.Record Records} in the cache.
33745      * @param {Function} fn The function to call. The {@link Ext.data.Record Record} is passed as the first parameter.
33746      * Returning <tt>false</tt> aborts and exits the iteration.
33747      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed.
33748      * Defaults to the current {@link Ext.data.Record Record} in the iteration.
33749      */
33750     each : function(fn, scope){
33751         this.data.each(fn, scope);
33752     },
33753
33754     /**
33755      * Gets all {@link Ext.data.Record records} modified since the last commit.  Modified records are
33756      * persisted across load operations (e.g., during paging). <b>Note</b>: deleted records are not
33757      * included.  See also <tt>{@link #pruneModifiedRecords}</tt> and
33758      * {@link Ext.data.Record}<tt>{@link Ext.data.Record#markDirty markDirty}.</tt>.
33759      * @return {Ext.data.Record[]} An array of {@link Ext.data.Record Records} containing outstanding
33760      * modifications.  To obtain modified fields within a modified record see
33761      *{@link Ext.data.Record}<tt>{@link Ext.data.Record#modified modified}.</tt>.
33762      */
33763     getModifiedRecords : function(){
33764         return this.modified;
33765     },
33766
33767     // private
33768     createFilterFn : function(property, value, anyMatch, caseSensitive){
33769         if(Ext.isEmpty(value, false)){
33770             return false;
33771         }
33772         value = this.data.createValueMatcher(value, anyMatch, caseSensitive);
33773         return function(r){
33774             return value.test(r.data[property]);
33775         };
33776     },
33777
33778     /**
33779      * Sums the value of <tt>property</tt> for each {@link Ext.data.Record record} between <tt>start</tt>
33780      * and <tt>end</tt> and returns the result.
33781      * @param {String} property A field in each record
33782      * @param {Number} start (optional) The record index to start at (defaults to <tt>0</tt>)
33783      * @param {Number} end (optional) The last record index to include (defaults to length - 1)
33784      * @return {Number} The sum
33785      */
33786     sum : function(property, start, end){
33787         var rs = this.data.items, v = 0;
33788         start = start || 0;
33789         end = (end || end === 0) ? end : rs.length-1;
33790
33791         for(var i = start; i <= end; i++){
33792             v += (rs[i].data[property] || 0);
33793         }
33794         return v;
33795     },
33796
33797     /**
33798      * Filter the {@link Ext.data.Record records} by a specified property.
33799      * @param {String} field A field on your records
33800      * @param {String/RegExp} value Either a string that the field should begin with, or a RegExp to test
33801      * against the field.
33802      * @param {Boolean} anyMatch (optional) <tt>true</tt> to match any part not just the beginning
33803      * @param {Boolean} caseSensitive (optional) <tt>true</tt> for case sensitive comparison
33804      */
33805     filter : function(property, value, anyMatch, caseSensitive){
33806         var fn = this.createFilterFn(property, value, anyMatch, caseSensitive);
33807         return fn ? this.filterBy(fn) : this.clearFilter();
33808     },
33809
33810     /**
33811      * Filter by a function. The specified function will be called for each
33812      * Record in this Store. If the function returns <tt>true</tt> the Record is included,
33813      * otherwise it is filtered out.
33814      * @param {Function} fn The function to be called. It will be passed the following parameters:<ul>
33815      * <li><b>record</b> : Ext.data.Record<p class="sub-desc">The {@link Ext.data.Record record}
33816      * to test for filtering. Access field values using {@link Ext.data.Record#get}.</p></li>
33817      * <li><b>id</b> : Object<p class="sub-desc">The ID of the Record passed.</p></li>
33818      * </ul>
33819      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to this Store.
33820      */
33821     filterBy : function(fn, scope){
33822         this.snapshot = this.snapshot || this.data;
33823         this.data = this.queryBy(fn, scope||this);
33824         this.fireEvent('datachanged', this);
33825     },
33826
33827     /**
33828      * Query the records by a specified property.
33829      * @param {String} field A field on your records
33830      * @param {String/RegExp} value Either a string that the field
33831      * should begin with, or a RegExp to test against the field.
33832      * @param {Boolean} anyMatch (optional) True to match any part not just the beginning
33833      * @param {Boolean} caseSensitive (optional) True for case sensitive comparison
33834      * @return {MixedCollection} Returns an Ext.util.MixedCollection of the matched records
33835      */
33836     query : function(property, value, anyMatch, caseSensitive){
33837         var fn = this.createFilterFn(property, value, anyMatch, caseSensitive);
33838         return fn ? this.queryBy(fn) : this.data.clone();
33839     },
33840
33841     /**
33842      * Query the cached records in this Store using a filtering function. The specified function
33843      * will be called with each record in this Store. If the function returns <tt>true</tt> the record is
33844      * included in the results.
33845      * @param {Function} fn The function to be called. It will be passed the following parameters:<ul>
33846      * <li><b>record</b> : Ext.data.Record<p class="sub-desc">The {@link Ext.data.Record record}
33847      * to test for filtering. Access field values using {@link Ext.data.Record#get}.</p></li>
33848      * <li><b>id</b> : Object<p class="sub-desc">The ID of the Record passed.</p></li>
33849      * </ul>
33850      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to this Store.
33851      * @return {MixedCollection} Returns an Ext.util.MixedCollection of the matched records
33852      **/
33853     queryBy : function(fn, scope){
33854         var data = this.snapshot || this.data;
33855         return data.filterBy(fn, scope||this);
33856     },
33857
33858     /**
33859      * Finds the index of the first matching Record in this store by a specific field value.
33860      * @param {String} fieldName The name of the Record field to test.
33861      * @param {String/RegExp} value Either a string that the field value
33862      * should begin with, or a RegExp to test against the field.
33863      * @param {Number} startIndex (optional) The index to start searching at
33864      * @param {Boolean} anyMatch (optional) True to match any part of the string, not just the beginning
33865      * @param {Boolean} caseSensitive (optional) True for case sensitive comparison
33866      * @return {Number} The matched index or -1
33867      */
33868     find : function(property, value, start, anyMatch, caseSensitive){
33869         var fn = this.createFilterFn(property, value, anyMatch, caseSensitive);
33870         return fn ? this.data.findIndexBy(fn, null, start) : -1;
33871     },
33872
33873     /**
33874      * Finds the index of the first matching Record in this store by a specific field value.
33875      * @param {String} fieldName The name of the Record field to test.
33876      * @param {Mixed} value The value to match the field against.
33877      * @param {Number} startIndex (optional) The index to start searching at
33878      * @return {Number} The matched index or -1
33879      */
33880     findExact: function(property, value, start){
33881         return this.data.findIndexBy(function(rec){
33882             return rec.get(property) === value;
33883         }, this, start);
33884     },
33885
33886     /**
33887      * Find the index of the first matching Record in this Store by a function.
33888      * If the function returns <tt>true</tt> it is considered a match.
33889      * @param {Function} fn The function to be called. It will be passed the following parameters:<ul>
33890      * <li><b>record</b> : Ext.data.Record<p class="sub-desc">The {@link Ext.data.Record record}
33891      * to test for filtering. Access field values using {@link Ext.data.Record#get}.</p></li>
33892      * <li><b>id</b> : Object<p class="sub-desc">The ID of the Record passed.</p></li>
33893      * </ul>
33894      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to this Store.
33895      * @param {Number} startIndex (optional) The index to start searching at
33896      * @return {Number} The matched index or -1
33897      */
33898     findBy : function(fn, scope, start){
33899         return this.data.findIndexBy(fn, scope, start);
33900     },
33901
33902     /**
33903      * Collects unique values for a particular dataIndex from this store.
33904      * @param {String} dataIndex The property to collect
33905      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
33906      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
33907      * @return {Array} An array of the unique values
33908      **/
33909     collect : function(dataIndex, allowNull, bypassFilter){
33910         var d = (bypassFilter === true && this.snapshot) ?
33911                 this.snapshot.items : this.data.items;
33912         var v, sv, r = [], l = {};
33913         for(var i = 0, len = d.length; i < len; i++){
33914             v = d[i].data[dataIndex];
33915             sv = String(v);
33916             if((allowNull || !Ext.isEmpty(v)) && !l[sv]){
33917                 l[sv] = true;
33918                 r[r.length] = v;
33919             }
33920         }
33921         return r;
33922     },
33923
33924     /**
33925      * Revert to a view of the Record cache with no filtering applied.
33926      * @param {Boolean} suppressEvent If <tt>true</tt> the filter is cleared silently without firing the
33927      * {@link #datachanged} event.
33928      */
33929     clearFilter : function(suppressEvent){
33930         if(this.isFiltered()){
33931             this.data = this.snapshot;
33932             delete this.snapshot;
33933             if(suppressEvent !== true){
33934                 this.fireEvent('datachanged', this);
33935             }
33936         }
33937     },
33938
33939     /**
33940      * Returns true if this store is currently filtered
33941      * @return {Boolean}
33942      */
33943     isFiltered : function(){
33944         return this.snapshot && this.snapshot != this.data;
33945     },
33946
33947     // private
33948     afterEdit : function(record){
33949         if(this.modified.indexOf(record) == -1){
33950             this.modified.push(record);
33951         }
33952         this.fireEvent('update', this, record, Ext.data.Record.EDIT);
33953     },
33954
33955     // private
33956     afterReject : function(record){
33957         this.modified.remove(record);
33958         this.fireEvent('update', this, record, Ext.data.Record.REJECT);
33959     },
33960
33961     // private
33962     afterCommit : function(record){
33963         this.modified.remove(record);
33964         this.fireEvent('update', this, record, Ext.data.Record.COMMIT);
33965     },
33966
33967     /**
33968      * Commit all Records with {@link #getModifiedRecords outstanding changes}. To handle updates for changes,
33969      * subscribe to the Store's {@link #update update event}, and perform updating when the third parameter is
33970      * Ext.data.Record.COMMIT.
33971      */
33972     commitChanges : function(){
33973         var m = this.modified.slice(0);
33974         this.modified = [];
33975         for(var i = 0, len = m.length; i < len; i++){
33976             m[i].commit();
33977         }
33978     },
33979
33980     /**
33981      * {@link Ext.data.Record#reject Reject} outstanding changes on all {@link #getModifiedRecords modified records}.
33982      */
33983     rejectChanges : function(){
33984         var m = this.modified.slice(0);
33985         this.modified = [];
33986         for(var i = 0, len = m.length; i < len; i++){
33987             m[i].reject();
33988         }
33989         var m = this.removed.slice(0).reverse();
33990         this.removed = [];
33991         for(var i = 0, len = m.length; i < len; i++){
33992             this.insert(m[i].lastIndex||0, m[i]);
33993             m[i].reject();
33994         }
33995     },
33996
33997     // private
33998     onMetaChange : function(meta){
33999         this.recordType = this.reader.recordType;
34000         this.fields = this.recordType.prototype.fields;
34001         delete this.snapshot;
34002         if(this.reader.meta.sortInfo){
34003             this.sortInfo = this.reader.meta.sortInfo;
34004         }else if(this.sortInfo  && !this.fields.get(this.sortInfo.field)){
34005             delete this.sortInfo;
34006         }
34007         if(this.writer){
34008             this.writer.meta = this.reader.meta;
34009         }
34010         this.modified = [];
34011         this.fireEvent('metachange', this, this.reader.meta);
34012     },
34013
34014     // private
34015     findInsertIndex : function(record){
34016         this.suspendEvents();
34017         var data = this.data.clone();
34018         this.data.add(record);
34019         this.applySort();
34020         var index = this.data.indexOf(record);
34021         this.data = data;
34022         this.resumeEvents();
34023         return index;
34024     },
34025
34026     /**
34027      * Set the value for a property name in this store's {@link #baseParams}.  Usage:</p><pre><code>
34028 myStore.setBaseParam('foo', {bar:3});
34029 </code></pre>
34030      * @param {String} name Name of the property to assign
34031      * @param {Mixed} value Value to assign the <tt>name</tt>d property
34032      **/
34033     setBaseParam : function (name, value){
34034         this.baseParams = this.baseParams || {};
34035         this.baseParams[name] = value;
34036     }
34037 });
34038
34039 Ext.reg('store', Ext.data.Store);
34040
34041 /**
34042  * @class Ext.data.Store.Error
34043  * @extends Ext.Error
34044  * Store Error extension.
34045  * @param {String} name
34046  */
34047 Ext.data.Store.Error = Ext.extend(Ext.Error, {
34048     name: 'Ext.data.Store'
34049 });
34050 Ext.apply(Ext.data.Store.Error.prototype, {
34051     lang: {
34052         'writer-undefined' : 'Attempted to execute a write-action without a DataWriter installed.'
34053     }
34054 });
34055 /**
34056  * @class Ext.data.Field
34057  * <p>This class encapsulates the field definition information specified in the field definition objects
34058  * passed to {@link Ext.data.Record#create}.</p>
34059  * <p>Developers do not need to instantiate this class. Instances are created by {@link Ext.data.Record.create}
34060  * and cached in the {@link Ext.data.Record#fields fields} property of the created Record constructor's <b>prototype.</b></p>
34061  */
34062 Ext.data.Field = function(config){
34063     if(typeof config == "string"){
34064         config = {name: config};
34065     }
34066     Ext.apply(this, config);
34067
34068     if(!this.type){
34069         this.type = "auto";
34070     }
34071
34072     var st = Ext.data.SortTypes;
34073     // named sortTypes are supported, here we look them up
34074     if(typeof this.sortType == "string"){
34075         this.sortType = st[this.sortType];
34076     }
34077
34078     // set default sortType for strings and dates
34079     if(!this.sortType){
34080         switch(this.type){
34081             case "string":
34082                 this.sortType = st.asUCString;
34083                 break;
34084             case "date":
34085                 this.sortType = st.asDate;
34086                 break;
34087             default:
34088                 this.sortType = st.none;
34089         }
34090     }
34091
34092     // define once
34093     var stripRe = /[\$,%]/g;
34094
34095     // prebuilt conversion function for this field, instead of
34096     // switching every time we're reading a value
34097     if(!this.convert){
34098         var cv, dateFormat = this.dateFormat;
34099         switch(this.type){
34100             case "":
34101             case "auto":
34102             case undefined:
34103                 cv = function(v){ return v; };
34104                 break;
34105             case "string":
34106                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
34107                 break;
34108             case "int":
34109                 cv = function(v){
34110                     return v !== undefined && v !== null && v !== '' ?
34111                         parseInt(String(v).replace(stripRe, ""), 10) : '';
34112                     };
34113                 break;
34114             case "float":
34115                 cv = function(v){
34116                     return v !== undefined && v !== null && v !== '' ?
34117                         parseFloat(String(v).replace(stripRe, ""), 10) : '';
34118                     };
34119                 break;
34120             case "bool":
34121                 cv = function(v){ return v === true || v === "true" || v == 1; };
34122                 break;
34123             case "date":
34124                 cv = function(v){
34125                     if(!v){
34126                         return '';
34127                     }
34128                     if(Ext.isDate(v)){
34129                         return v;
34130                     }
34131                     if(dateFormat){
34132                         if(dateFormat == "timestamp"){
34133                             return new Date(v*1000);
34134                         }
34135                         if(dateFormat == "time"){
34136                             return new Date(parseInt(v, 10));
34137                         }
34138                         return Date.parseDate(v, dateFormat);
34139                     }
34140                     var parsed = Date.parse(v);
34141                     return parsed ? new Date(parsed) : null;
34142                 };
34143                 break;
34144             default:
34145                 cv = function(v){ return v; };
34146                 break;
34147
34148         }
34149         this.convert = cv;
34150     }
34151 };
34152
34153 Ext.data.Field.prototype = {
34154     /**
34155      * @cfg {String} name
34156      * The name by which the field is referenced within the Record. This is referenced by, for example,
34157      * the <tt>dataIndex</tt> property in column definition objects passed to {@link Ext.grid.ColumnModel}.
34158      * <p>Note: In the simplest case, if no properties other than <tt>name</tt> are required, a field
34159      * definition may consist of just a String for the field name.</p>
34160      */
34161     /**
34162      * @cfg {String} type
34163      * (Optional) The data type for conversion to displayable value if <tt>{@link Ext.data.Field#convert convert}</tt>
34164      * has not been specified. Possible values are
34165      * <div class="mdetail-params"><ul>
34166      * <li>auto (Default, implies no conversion)</li>
34167      * <li>string</li>
34168      * <li>int</li>
34169      * <li>float</li>
34170      * <li>boolean</li>
34171      * <li>date</li></ul></div>
34172      */
34173     /**
34174      * @cfg {Function} convert
34175      * (Optional) A function which converts the value provided by the Reader into an object that will be stored
34176      * in the Record. It is passed the following parameters:<div class="mdetail-params"><ul>
34177      * <li><b>v</b> : Mixed<div class="sub-desc">The data value as read by the Reader, if undefined will use
34178      * the configured <tt>{@link Ext.data.Field#defaultValue defaultValue}</tt>.</div></li>
34179      * <li><b>rec</b> : Mixed<div class="sub-desc">The data object containing the row as read by the Reader.
34180      * Depending on the Reader type, this could be an Array ({@link Ext.data.ArrayReader ArrayReader}), an object
34181      *  ({@link Ext.data.JsonReader JsonReader}), or an XML element ({@link Ext.data.XMLReader XMLReader}).</div></li>
34182      * </ul></div>
34183      * <pre><code>
34184 // example of convert function
34185 function fullName(v, record){
34186     return record.name.last + ', ' + record.name.first;
34187 }
34188
34189 function location(v, record){
34190     return !record.city ? '' : (record.city + ', ' + record.state);
34191 }
34192
34193 var Dude = Ext.data.Record.create([
34194     {name: 'fullname',  convert: fullName},
34195     {name: 'firstname', mapping: 'name.first'},
34196     {name: 'lastname',  mapping: 'name.last'},
34197     {name: 'city', defaultValue: 'homeless'},
34198     'state',
34199     {name: 'location',  convert: location}
34200 ]);
34201
34202 // create the data store
34203 var store = new Ext.data.Store({
34204     reader: new Ext.data.JsonReader(
34205         {
34206             idProperty: 'key',
34207             root: 'daRoot',
34208             totalProperty: 'total'
34209         },
34210         Dude  // recordType
34211     )
34212 });
34213
34214 var myData = [
34215     { key: 1,
34216       name: { first: 'Fat',    last:  'Albert' }
34217       // notice no city, state provided in data object
34218     },
34219     { key: 2,
34220       name: { first: 'Barney', last:  'Rubble' },
34221       city: 'Bedrock', state: 'Stoneridge'
34222     },
34223     { key: 3,
34224       name: { first: 'Cliff',  last:  'Claven' },
34225       city: 'Boston',  state: 'MA'
34226     }
34227 ];
34228      * </code></pre>
34229      */
34230     /**
34231      * @cfg {String} dateFormat
34232      * (Optional) A format string for the {@link Date#parseDate Date.parseDate} function, or "timestamp" if the
34233      * value provided by the Reader is a UNIX timestamp, or "time" if the value provided by the Reader is a
34234      * javascript millisecond timestamp.
34235      */
34236     dateFormat: null,
34237     /**
34238      * @cfg {Mixed} defaultValue
34239      * (Optional) The default value used <b>when a Record is being created by a {@link Ext.data.Reader Reader}</b>
34240      * when the item referenced by the <tt>{@link Ext.data.Field#mapping mapping}</tt> does not exist in the data
34241      * object (i.e. undefined). (defaults to "")
34242      */
34243     defaultValue: "",
34244     /**
34245      * @cfg {String/Number} mapping
34246      * <p>(Optional) A path expression for use by the {@link Ext.data.DataReader} implementation
34247      * that is creating the {@link Ext.data.Record Record} to extract the Field value from the data object.
34248      * If the path expression is the same as the field name, the mapping may be omitted.</p>
34249      * <p>The form of the mapping expression depends on the Reader being used.</p>
34250      * <div class="mdetail-params"><ul>
34251      * <li>{@link Ext.data.JsonReader}<div class="sub-desc">The mapping is a string containing the javascript
34252      * expression to reference the data from an element of the data item's {@link Ext.data.JsonReader#root root} Array. Defaults to the field name.</div></li>
34253      * <li>{@link Ext.data.XmlReader}<div class="sub-desc">The mapping is an {@link Ext.DomQuery} path to the data
34254      * item relative to the DOM element that represents the {@link Ext.data.XmlReader#record record}. Defaults to the field name.</div></li>
34255      * <li>{@link Ext.data.ArrayReader}<div class="sub-desc">The mapping is a number indicating the Array index
34256      * of the field's value. Defaults to the field specification's Array position.</div></li>
34257      * </ul></div>
34258      * <p>If a more complex value extraction strategy is required, then configure the Field with a {@link #convert}
34259      * function. This is passed the whole row object, and may interrogate it in whatever way is necessary in order to
34260      * return the desired data.</p>
34261      */
34262     mapping: null,
34263     /**
34264      * @cfg {Function} sortType
34265      * (Optional) A function which converts a Field's value to a comparable value in order to ensure
34266      * correct sort ordering. Predefined functions are provided in {@link Ext.data.SortTypes}. A custom
34267      * sort example:<pre><code>
34268 // current sort     after sort we want
34269 // +-+------+          +-+------+
34270 // |1|First |          |1|First |
34271 // |2|Last  |          |3|Second|
34272 // |3|Second|          |2|Last  |
34273 // +-+------+          +-+------+
34274
34275 sortType: function(value) {
34276    switch (value.toLowerCase()) // native toLowerCase():
34277    {
34278       case 'first': return 1;
34279       case 'second': return 2;
34280       default: return 3;
34281    }
34282 }
34283      * </code></pre>
34284      */
34285     sortType : null,
34286     /**
34287      * @cfg {String} sortDir
34288      * (Optional) Initial direction to sort (<tt>"ASC"</tt> or  <tt>"DESC"</tt>).  Defaults to
34289      * <tt>"ASC"</tt>.
34290      */
34291     sortDir : "ASC",
34292     /**
34293      * @cfg {Boolean} allowBlank
34294      * (Optional) Used for validating a {@link Ext.data.Record record}, defaults to <tt>true</tt>.
34295      * An empty value here will cause {@link Ext.data.Record}.{@link Ext.data.Record#isValid isValid}
34296      * to evaluate to <tt>false</tt>.
34297      */
34298     allowBlank : true
34299 };/**\r
34300  * @class Ext.data.DataReader\r
34301  * Abstract base class for reading structured data from a data source and converting\r
34302  * it into an object containing {@link Ext.data.Record} objects and metadata for use\r
34303  * by an {@link Ext.data.Store}.  This class is intended to be extended and should not\r
34304  * be created directly. For existing implementations, see {@link Ext.data.ArrayReader},\r
34305  * {@link Ext.data.JsonReader} and {@link Ext.data.XmlReader}.\r
34306  * @constructor Create a new DataReader\r
34307  * @param {Object} meta Metadata configuration options (implementation-specific).\r
34308  * @param {Array/Object} recordType\r
34309  * <p>Either an Array of {@link Ext.data.Field Field} definition objects (which\r
34310  * will be passed to {@link Ext.data.Record#create}, or a {@link Ext.data.Record Record}\r
34311  * constructor created using {@link Ext.data.Record#create}.</p>\r
34312  */\r
34313 Ext.data.DataReader = function(meta, recordType){\r
34314     /**\r
34315      * This DataReader's configured metadata as passed to the constructor.\r
34316      * @type Mixed\r
34317      * @property meta\r
34318      */\r
34319     this.meta = meta;\r
34320     /**\r
34321      * @cfg {Array/Object} fields\r
34322      * <p>Either an Array of {@link Ext.data.Field Field} definition objects (which\r
34323      * will be passed to {@link Ext.data.Record#create}, or a {@link Ext.data.Record Record}\r
34324      * constructor created from {@link Ext.data.Record#create}.</p>\r
34325      */\r
34326     this.recordType = Ext.isArray(recordType) ?\r
34327         Ext.data.Record.create(recordType) : recordType;\r
34328
34329     // if recordType defined make sure extraction functions are defined\r
34330     if (this.recordType){\r
34331         this.buildExtractors();\r
34332     }
34333 };\r
34334 \r
34335 Ext.data.DataReader.prototype = {\r
34336     /**\r
34337      * @cfg {String} messageProperty [undefined] Optional name of a property within a server-response that represents a user-feedback message.\r
34338      */\r
34339     /**\r
34340      * Abstract method created in extension's buildExtractors impl.\r
34341      */\r
34342     getTotal: Ext.emptyFn,\r
34343     /**\r
34344      * Abstract method created in extension's buildExtractors impl.\r
34345      */\r
34346     getRoot: Ext.emptyFn,\r
34347     /**\r
34348      * Abstract method created in extension's buildExtractors impl.\r
34349      */\r
34350     getMessage: Ext.emptyFn,\r
34351     /**\r
34352      * Abstract method created in extension's buildExtractors impl.\r
34353      */\r
34354     getSuccess: Ext.emptyFn,\r
34355     /**\r
34356      * Abstract method created in extension's buildExtractors impl.\r
34357      */\r
34358     getId: Ext.emptyFn,\r
34359     /**\r
34360      * Abstract method, overridden in DataReader extensions such as {@link Ext.data.JsonReader} and {@link Ext.data.XmlReader}\r
34361      */\r
34362     buildExtractors : Ext.emptyFn,\r
34363     /**\r
34364      * Abstract method overridden in DataReader extensions such as {@link Ext.data.JsonReader} and {@link Ext.data.XmlReader}\r
34365      */\r
34366     extractData : Ext.emptyFn,\r
34367     /**\r
34368      * Abstract method overridden in DataReader extensions such as {@link Ext.data.JsonReader} and {@link Ext.data.XmlReader}\r
34369      */\r
34370     extractValues : Ext.emptyFn,\r
34371 \r
34372     /**\r
34373      * Used for un-phantoming a record after a successful database insert.  Sets the records pk along with new data from server.\r
34374      * You <b>must</b> return at least the database pk using the idProperty defined in your DataReader configuration.  The incoming\r
34375      * data from server will be merged with the data in the local record.\r
34376      * In addition, you <b>must</b> return record-data from the server in the same order received.\r
34377      * Will perform a commit as well, un-marking dirty-fields.  Store's "update" event will be suppressed.\r
34378      * @param {Record/Record[]} record The phantom record to be realized.\r
34379      * @param {Object/Object[]} data The new record data to apply.  Must include the primary-key from database defined in idProperty field.\r
34380      */\r
34381     realize: function(rs, data){\r
34382         if (Ext.isArray(rs)) {\r
34383             for (var i = rs.length - 1; i >= 0; i--) {\r
34384                 // recurse\r
34385                 if (Ext.isArray(data)) {\r
34386                     this.realize(rs.splice(i,1).shift(), data.splice(i,1).shift());\r
34387                 }\r
34388                 else {\r
34389                     // weird...rs is an array but data isn't??  recurse but just send in the whole invalid data object.\r
34390                     // the else clause below will detect !this.isData and throw exception.\r
34391                     this.realize(rs.splice(i,1).shift(), data);\r
34392                 }\r
34393             }\r
34394         }\r
34395         else {\r
34396             // If rs is NOT an array but data IS, see if data contains just 1 record.  If so extract it and carry on.\r
34397             if (Ext.isArray(data) && data.length == 1) {\r
34398                 data = data.shift();\r
34399             }\r
34400             if (!this.isData(data)) {\r
34401                 // TODO: Let exception-handler choose to commit or not rather than blindly rs.commit() here.\r
34402                 //rs.commit();\r
34403                 throw new Ext.data.DataReader.Error('realize', rs);\r
34404             }\r
34405             rs.phantom = false; // <-- That's what it's all about\r
34406             rs._phid = rs.id;  // <-- copy phantom-id -> _phid, so we can remap in Store#onCreateRecords\r
34407             rs.id = this.getId(data);\r
34408 \r
34409             rs.fields.each(function(f) {\r
34410                 if (data[f.name] !== f.defaultValue) {\r
34411                     rs.data[f.name] = data[f.name];\r
34412                 }\r
34413             });\r
34414             rs.commit();\r
34415         }\r
34416     },\r
34417 \r
34418     /**\r
34419      * Used for updating a non-phantom or "real" record's data with fresh data from server after remote-save.\r
34420      * If returning data from multiple-records after a batch-update, you <b>must</b> return record-data from the server in\r
34421      * the same order received.  Will perform a commit as well, un-marking dirty-fields.  Store's "update" event will be\r
34422      * suppressed as the record receives fresh new data-hash\r
34423      * @param {Record/Record[]} rs\r
34424      * @param {Object/Object[]} data\r
34425      */\r
34426     update : function(rs, data) {\r
34427         if (Ext.isArray(rs)) {\r
34428             for (var i=rs.length-1; i >= 0; i--) {\r
34429                 if (Ext.isArray(data)) {\r
34430                     this.update(rs.splice(i,1).shift(), data.splice(i,1).shift());\r
34431                 }\r
34432                 else {\r
34433                     // weird...rs is an array but data isn't??  recurse but just send in the whole data object.\r
34434                     // the else clause below will detect !this.isData and throw exception.\r
34435                     this.update(rs.splice(i,1).shift(), data);\r
34436                 }\r
34437             }\r
34438         }\r
34439         else {\r
34440             // If rs is NOT an array but data IS, see if data contains just 1 record.  If so extract it and carry on.\r
34441             if (Ext.isArray(data) && data.length == 1) {\r
34442                 data = data.shift();\r
34443             }\r
34444             if (this.isData(data)) {\r
34445                 rs.fields.each(function(f) {\r
34446                     if (data[f.name] !== f.defaultValue) {\r
34447                         rs.data[f.name] = data[f.name];\r
34448                     }\r
34449                 });\r
34450             }\r
34451             rs.commit();\r
34452         }\r
34453     },\r
34454 \r
34455     /**\r
34456      * returns extracted, type-cast rows of data.  Iterates to call #extractValues for each row\r
34457      * @param {Object[]/Object} data-root from server response\r
34458      * @param {Boolean} returnRecords [false] Set true to return instances of Ext.data.Record\r
34459      * @private\r
34460      */\r
34461     extractData : function(root, returnRecords) {\r
34462         // A bit ugly this, too bad the Record's raw data couldn't be saved in a common property named "raw" or something.\r
34463         var rawName = (this instanceof Ext.data.JsonReader) ? 'json' : 'node';\r
34464 \r
34465         var rs = [];\r
34466 \r
34467         // Had to add Check for XmlReader, #isData returns true if root is an Xml-object.  Want to check in order to re-factor\r
34468         // #extractData into DataReader base, since the implementations are almost identical for JsonReader, XmlReader\r
34469         if (this.isData(root) && !(this instanceof Ext.data.XmlReader)) {\r
34470             root = [root];\r
34471         }\r
34472         var f       = this.recordType.prototype.fields,\r
34473             fi      = f.items,\r
34474             fl      = f.length,\r
34475             rs      = [];\r
34476         if (returnRecords === true) {\r
34477             var Record = this.recordType;\r
34478             for (var i = 0; i < root.length; i++) {\r
34479                 var n = root[i];\r
34480                 var record = new Record(this.extractValues(n, fi, fl), this.getId(n));\r
34481                 record[rawName] = n;    // <-- There's implementation of ugly bit, setting the raw record-data.\r
34482                 rs.push(record);\r
34483             }\r
34484         }\r
34485         else {\r
34486             for (var i = 0; i < root.length; i++) {\r
34487                 var data = this.extractValues(root[i], fi, fl);\r
34488                 data[this.meta.idProperty] = this.getId(root[i]);\r
34489                 rs.push(data);\r
34490             }\r
34491         }\r
34492         return rs;\r
34493     },\r
34494 \r
34495     /**\r
34496      * Returns true if the supplied data-hash <b>looks</b> and quacks like data.  Checks to see if it has a key\r
34497      * corresponding to idProperty defined in your DataReader config containing non-empty pk.\r
34498      * @param {Object} data\r
34499      * @return {Boolean}\r
34500      */\r
34501     isData : function(data) {\r
34502         return (data && Ext.isObject(data) && !Ext.isEmpty(this.getId(data))) ? true : false;\r
34503     },\r
34504 \r
34505     // private function a store will createSequence upon\r
34506     onMetaChange : function(meta){\r
34507         delete this.ef;\r
34508         this.meta = meta;\r
34509         this.recordType = Ext.data.Record.create(meta.fields);\r
34510         this.buildExtractors();\r
34511     }\r
34512 };\r
34513 \r
34514 /**\r
34515  * @class Ext.data.DataReader.Error\r
34516  * @extends Ext.Error\r
34517  * General error class for Ext.data.DataReader\r
34518  */\r
34519 Ext.data.DataReader.Error = Ext.extend(Ext.Error, {\r
34520     constructor : function(message, arg) {\r
34521         this.arg = arg;\r
34522         Ext.Error.call(this, message);\r
34523     },\r
34524     name: 'Ext.data.DataReader'\r
34525 });\r
34526 Ext.apply(Ext.data.DataReader.Error.prototype, {\r
34527     lang : {\r
34528         'update': "#update received invalid data from server.  Please see docs for DataReader#update and review your DataReader configuration.",\r
34529         'realize': "#realize was called with invalid remote-data.  Please see the docs for DataReader#realize and review your DataReader configuration.",\r
34530         'invalid-response': "#readResponse received an invalid response from the server."\r
34531     }\r
34532 });\r
34533 /**
34534  * @class Ext.data.DataWriter
34535  * <p>Ext.data.DataWriter facilitates create, update, and destroy actions between
34536  * an Ext.data.Store and a server-side framework. A Writer enabled Store will
34537  * automatically manage the Ajax requests to perform CRUD actions on a Store.</p>
34538  * <p>Ext.data.DataWriter is an abstract base class which is intended to be extended
34539  * and should not be created directly. For existing implementations, see
34540  * {@link Ext.data.JsonWriter}.</p>
34541  * <p>Creating a writer is simple:</p>
34542  * <pre><code>
34543 var writer = new Ext.data.JsonWriter({
34544     encode: false   // &lt;--- false causes data to be printed to jsonData config-property of Ext.Ajax#reqeust
34545 });
34546  * </code></pre>
34547  * * <p>Same old JsonReader as Ext-2.x:</p>
34548  * <pre><code>
34549 var reader = new Ext.data.JsonReader({idProperty: 'id'}, [{name: 'first'}, {name: 'last'}, {name: 'email'}]);
34550  * </code></pre>
34551  *
34552  * <p>The proxy for a writer enabled store can be configured with a simple <code>url</code>:</p>
34553  * <pre><code>
34554 // Create a standard HttpProxy instance.
34555 var proxy = new Ext.data.HttpProxy({
34556     url: 'app.php/users'    // &lt;--- Supports "provides"-type urls, such as '/users.json', '/products.xml' (Hello Rails/Merb)
34557 });
34558  * </code></pre>
34559  * <p>For finer grained control, the proxy may also be configured with an <code>API</code>:</p>
34560  * <pre><code>
34561 // Maximum flexibility with the API-configuration
34562 var proxy = new Ext.data.HttpProxy({
34563     api: {
34564         read    : 'app.php/users/read',
34565         create  : 'app.php/users/create',
34566         update  : 'app.php/users/update',
34567         destroy : {  // &lt;--- Supports object-syntax as well
34568             url: 'app.php/users/destroy',
34569             method: "DELETE"
34570         }
34571     }
34572 });
34573  * </code></pre>
34574  * <p>Pulling it all together into a Writer-enabled Store:</p>
34575  * <pre><code>
34576 var store = new Ext.data.Store({
34577     proxy: proxy,
34578     reader: reader,
34579     writer: writer,
34580     autoLoad: true,
34581     autoSave: true  // -- Cell-level updates.
34582 });
34583  * </code></pre>
34584  * <p>Initiating write-actions <b>automatically</b>, using the existing Ext2.0 Store/Record API:</p>
34585  * <pre><code>
34586 var rec = store.getAt(0);
34587 rec.set('email', 'foo@bar.com');  // &lt;--- Immediately initiates an UPDATE action through configured proxy.
34588
34589 store.remove(rec);  // &lt;---- Immediately initiates a DESTROY action through configured proxy.
34590  * </code></pre>
34591  * <p>For <b>record/batch</b> updates, use the Store-configuration {@link Ext.data.Store#autoSave autoSave:false}</p>
34592  * <pre><code>
34593 var store = new Ext.data.Store({
34594     proxy: proxy,
34595     reader: reader,
34596     writer: writer,
34597     autoLoad: true,
34598     autoSave: false  // -- disable cell-updates
34599 });
34600
34601 var urec = store.getAt(0);
34602 urec.set('email', 'foo@bar.com');
34603
34604 var drec = store.getAt(1);
34605 store.remove(drec);
34606
34607 // Push the button!
34608 store.save();
34609  * </code></pre>
34610  * @constructor Create a new DataWriter
34611  * @param {Object} meta Metadata configuration options (implementation-specific)
34612  * @param {Object} recordType Either an Array of field definition objects as specified
34613  * in {@link Ext.data.Record#create}, or an {@link Ext.data.Record} object created
34614  * using {@link Ext.data.Record#create}.
34615  */
34616 Ext.data.DataWriter = function(config){
34617     Ext.apply(this, config);
34618 };
34619 Ext.data.DataWriter.prototype = {
34620
34621     /**
34622      * @cfg {Boolean} writeAllFields
34623      * <tt>false</tt> by default.  Set <tt>true</tt> to have DataWriter return ALL fields of a modified
34624      * record -- not just those that changed.
34625      * <tt>false</tt> to have DataWriter only request modified fields from a record.
34626      */
34627     writeAllFields : false,
34628     /**
34629      * @cfg {Boolean} listful
34630      * <tt>false</tt> by default.  Set <tt>true</tt> to have the DataWriter <b>always</b> write HTTP params as a list,
34631      * even when acting upon a single record.
34632      */
34633     listful : false,    // <-- listful is actually not used internally here in DataWriter.  @see Ext.data.Store#execute.
34634
34635     /**
34636      * Compiles a Store recordset into a data-format defined by an extension such as {@link Ext.data.JsonWriter} or {@link Ext.data.XmlWriter} in preparation for a {@link Ext.data.Api#actions server-write action}.  The first two params are similar similar in nature to {@link Ext#apply},
34637      * Where the first parameter is the <i>receiver</i> of paramaters and the second, baseParams, <i>the source</i>.
34638      * @param {Object} params The request-params receiver.
34639      * @param {Object} baseParams as defined by {@link Ext.data.Store#baseParams}.  The baseParms must be encoded by the extending class, eg: {@link Ext.data.JsonWriter}, {@link Ext.data.XmlWriter}.
34640      * @param {String} action [{@link Ext.data.Api#actions create|update|destroy}]
34641      * @param {Record/Record[]} rs The recordset to write, the subject(s) of the write action.
34642      */
34643     apply : function(params, baseParams, action, rs) {
34644         var data    = [],
34645         renderer    = action + 'Record';
34646         // TODO implement @cfg listful here
34647         if (Ext.isArray(rs)) {
34648             Ext.each(rs, function(rec){
34649                 data.push(this[renderer](rec));
34650             }, this);
34651         }
34652         else if (rs instanceof Ext.data.Record) {
34653             data = this[renderer](rs);
34654         }
34655         this.render(params, baseParams, data);
34656     },
34657
34658     /**
34659      * abstract method meant to be overridden by all DataWriter extensions.  It's the extension's job to apply the "data" to the "params".
34660      * The data-object provided to render is populated with data according to the meta-info defined in the user's DataReader config,
34661      * @param {String} action [Ext.data.Api.actions.create|read|update|destroy]
34662      * @param {Record[]} rs Store recordset
34663      * @param {Object} params Http params to be sent to server.
34664      * @param {Object} data object populated according to DataReader meta-data.
34665      */
34666     render : Ext.emptyFn,
34667
34668     /**
34669      * @cfg {Function} updateRecord Abstract method that should be implemented in all subclasses
34670      * (e.g.: {@link Ext.data.JsonWriter#updateRecord JsonWriter.updateRecord}
34671      */
34672     updateRecord : Ext.emptyFn,
34673
34674     /**
34675      * @cfg {Function} createRecord Abstract method that should be implemented in all subclasses
34676      * (e.g.: {@link Ext.data.JsonWriter#createRecord JsonWriter.createRecord})
34677      */
34678     createRecord : Ext.emptyFn,
34679
34680     /**
34681      * @cfg {Function} destroyRecord Abstract method that should be implemented in all subclasses
34682      * (e.g.: {@link Ext.data.JsonWriter#destroyRecord JsonWriter.destroyRecord})
34683      */
34684     destroyRecord : Ext.emptyFn,
34685
34686     /**
34687      * Converts a Record to a hash, taking into account the state of the Ext.data.Record along with configuration properties
34688      * related to its rendering, such as {@link #writeAllFields}, {@link Ext.data.Record#phantom phantom}, {@link Ext.data.Record#getChanges getChanges} and
34689      * {@link Ext.data.DataReader#idProperty idProperty}
34690      * @param {Ext.data.Record}
34691      * @param {Object} config <b>NOT YET IMPLEMENTED</b>.  Will implement an exlude/only configuration for fine-control over which fields do/don't get rendered.
34692      * @return {Object}
34693      * @protected
34694      * TODO Implement excludes/only configuration with 2nd param?
34695      */
34696     toHash : function(rec, config) {
34697         var map = rec.fields.map,
34698             data = {},
34699             raw = (this.writeAllFields === false && rec.phantom === false) ? rec.getChanges() : rec.data,
34700             m;
34701         Ext.iterate(raw, function(prop, value){
34702             if((m = map[prop])){
34703                 data[m.mapping ? m.mapping : m.name] = value;
34704             }
34705         });
34706         // we don't want to write Ext auto-generated id to hash.  Careful not to remove it on Models not having auto-increment pk though.
34707         // We can tell its not auto-increment if the user defined a DataReader field for it *and* that field's value is non-empty.
34708         // we could also do a RegExp here for the Ext.data.Record AUTO_ID prefix.
34709         if (rec.phantom) {
34710             if (rec.fields.containsKey(this.meta.idProperty) && Ext.isEmpty(rec.data[this.meta.idProperty])) {
34711                 delete data[this.meta.idProperty];
34712             }
34713         } else {
34714             data[this.meta.idProperty] = rec.id
34715         }
34716         return data;
34717     },
34718
34719     /**
34720      * Converts a {@link Ext.data.DataWriter#toHash Hashed} {@link Ext.data.Record} to fields-array array suitable
34721      * for encoding to xml via XTemplate, eg:
34722 <code><pre>&lt;tpl for=".">&lt;{name}>{value}&lt;/{name}&lt;/tpl></pre></code>
34723      * eg, <b>non-phantom</b>:
34724 <code><pre>{id: 1, first: 'foo', last: 'bar'} --> [{name: 'id', value: 1}, {name: 'first', value: 'foo'}, {name: 'last', value: 'bar'}]</pre></code>
34725      * {@link Ext.data.Record#phantom Phantom} records will have had their idProperty omitted in {@link #toHash} if determined to be auto-generated.
34726      * Non AUTOINCREMENT pks should have been protected.
34727      * @param {Hash} data Hashed by Ext.data.DataWriter#toHash
34728      * @return {[Object]} Array of attribute-objects.
34729      * @protected
34730      */
34731     toArray : function(data) {
34732         var fields = [];
34733         Ext.iterate(data, function(k, v) {fields.push({name: k, value: v});},this);
34734         return fields;
34735     }
34736 };/**\r
34737  * @class Ext.data.DataProxy\r
34738  * @extends Ext.util.Observable\r
34739  * <p>Abstract base class for implementations which provide retrieval of unformatted data objects.\r
34740  * This class is intended to be extended and should not be created directly. For existing implementations,\r
34741  * see {@link Ext.data.DirectProxy}, {@link Ext.data.HttpProxy}, {@link Ext.data.ScriptTagProxy} and\r
34742  * {@link Ext.data.MemoryProxy}.</p>\r
34743  * <p>DataProxy implementations are usually used in conjunction with an implementation of {@link Ext.data.DataReader}\r
34744  * (of the appropriate type which knows how to parse the data object) to provide a block of\r
34745  * {@link Ext.data.Records} to an {@link Ext.data.Store}.</p>\r
34746  * <p>The parameter to a DataProxy constructor may be an {@link Ext.data.Connection} or can also be the\r
34747  * config object to an {@link Ext.data.Connection}.</p>\r
34748  * <p>Custom implementations must implement either the <code><b>doRequest</b></code> method (preferred) or the\r
34749  * <code>load</code> method (deprecated). See\r
34750  * {@link Ext.data.HttpProxy}.{@link Ext.data.HttpProxy#doRequest doRequest} or\r
34751  * {@link Ext.data.HttpProxy}.{@link Ext.data.HttpProxy#load load} for additional details.</p>\r
34752  * <p><b><u>Example 1</u></b></p>\r
34753  * <pre><code>\r
34754 proxy: new Ext.data.ScriptTagProxy({\r
34755     {@link Ext.data.Connection#url url}: 'http://extjs.com/forum/topics-remote.php'\r
34756 }),\r
34757  * </code></pre>\r
34758  * <p><b><u>Example 2</u></b></p>\r
34759  * <pre><code>\r
34760 proxy : new Ext.data.HttpProxy({\r
34761     {@link Ext.data.Connection#method method}: 'GET',\r
34762     {@link Ext.data.HttpProxy#prettyUrls prettyUrls}: false,\r
34763     {@link Ext.data.Connection#url url}: 'local/default.php', // see options parameter for {@link Ext.Ajax#request}\r
34764     {@link #api}: {\r
34765         // all actions except the following will use above url\r
34766         create  : 'local/new.php',\r
34767         update  : 'local/update.php'\r
34768     }\r
34769 }),\r
34770  * </code></pre>\r
34771  * <p>And <b>new in Ext version 3</b>, attach centralized event-listeners upon the DataProxy class itself!  This is a great place\r
34772  * to implement a <i>messaging system</i> to centralize your application's user-feedback and error-handling.</p>\r
34773  * <pre><code>\r
34774 // Listen to all "beforewrite" event fired by all proxies.\r
34775 Ext.data.DataProxy.on('beforewrite', function(proxy, action) {\r
34776     console.log('beforewrite: ', action);\r
34777 });\r
34778 \r
34779 // Listen to "write" event fired by all proxies\r
34780 Ext.data.DataProxy.on('write', function(proxy, action, data, res, rs) {\r
34781     console.info('write: ', action);\r
34782 });\r
34783 \r
34784 // Listen to "exception" event fired by all proxies\r
34785 Ext.data.DataProxy.on('exception', function(proxy, type, action) {\r
34786     console.error(type + action + ' exception);\r
34787 });\r
34788  * </code></pre>\r
34789  * <b>Note:</b> These three events are all fired with the signature of the corresponding <i>DataProxy instance</i> event {@link #beforewrite beforewrite}, {@link #write write} and {@link #exception exception}.\r
34790  */\r
34791 Ext.data.DataProxy = function(conn){\r
34792     // make sure we have a config object here to support ux proxies.\r
34793     // All proxies should now send config into superclass constructor.\r
34794     conn = conn || {};\r
34795 \r
34796     // This line caused a bug when people use custom Connection object having its own request method.\r
34797     // http://extjs.com/forum/showthread.php?t=67194.  Have to set DataProxy config\r
34798     //Ext.applyIf(this, conn);\r
34799 \r
34800     this.api     = conn.api;\r
34801     this.url     = conn.url;\r
34802     this.restful = conn.restful;\r
34803     this.listeners = conn.listeners;\r
34804 \r
34805     // deprecated\r
34806     this.prettyUrls = conn.prettyUrls;\r
34807 \r
34808     /**\r
34809      * @cfg {Object} api\r
34810      * Specific urls to call on CRUD action methods "read", "create", "update" and "destroy".\r
34811      * Defaults to:<pre><code>\r
34812 api: {\r
34813     read    : undefined,\r
34814     create  : undefined,\r
34815     update  : undefined,\r
34816     destroy : undefined\r
34817 }\r
34818      * </code></pre>\r
34819      * <p>The url is built based upon the action being executed <tt>[load|create|save|destroy]</tt>\r
34820      * using the commensurate <tt>{@link #api}</tt> property, or if undefined default to the\r
34821      * configured {@link Ext.data.Store}.{@link Ext.data.Store#url url}.</p><br>\r
34822      * <p>For example:</p>\r
34823      * <pre><code>\r
34824 api: {\r
34825     load :    '/controller/load',\r
34826     create :  '/controller/new',  // Server MUST return idProperty of new record\r
34827     save :    '/controller/update',\r
34828     destroy : '/controller/destroy_action'\r
34829 }\r
34830 \r
34831 // Alternatively, one can use the object-form to specify each API-action\r
34832 api: {\r
34833     load: {url: 'read.php', method: 'GET'},\r
34834     create: 'create.php',\r
34835     destroy: 'destroy.php',\r
34836     save: 'update.php'\r
34837 }\r
34838      * </code></pre>\r
34839      * <p>If the specific URL for a given CRUD action is undefined, the CRUD action request\r
34840      * will be directed to the configured <tt>{@link Ext.data.Connection#url url}</tt>.</p>\r
34841      * <br><p><b>Note</b>: To modify the URL for an action dynamically the appropriate API\r
34842      * property should be modified before the action is requested using the corresponding before\r
34843      * action event.  For example to modify the URL associated with the load action:\r
34844      * <pre><code>\r
34845 // modify the url for the action\r
34846 myStore.on({\r
34847     beforeload: {\r
34848         fn: function (store, options) {\r
34849             // use <tt>{@link Ext.data.HttpProxy#setUrl setUrl}</tt> to change the URL for *just* this request.\r
34850             store.proxy.setUrl('changed1.php');\r
34851 \r
34852             // set optional second parameter to true to make this URL change\r
34853             // permanent, applying this URL for all subsequent requests.\r
34854             store.proxy.setUrl('changed1.php', true);\r
34855 \r
34856             // Altering the proxy API should be done using the public\r
34857             // method <tt>{@link Ext.data.DataProxy#setApi setApi}</tt>.\r
34858             store.proxy.setApi('read', 'changed2.php');\r
34859 \r
34860             // Or set the entire API with a config-object.\r
34861             // When using the config-object option, you must redefine the <b>entire</b>\r
34862             // API -- not just a specific action of it.\r
34863             store.proxy.setApi({\r
34864                 read    : 'changed_read.php',\r
34865                 create  : 'changed_create.php',\r
34866                 update  : 'changed_update.php',\r
34867                 destroy : 'changed_destroy.php'\r
34868             });\r
34869         }\r
34870     }\r
34871 });\r
34872      * </code></pre>\r
34873      * </p>\r
34874      */\r
34875 \r
34876     this.addEvents(\r
34877         /**\r
34878          * @event exception\r
34879          * <p>Fires if an exception occurs in the Proxy during a remote request. This event is relayed\r
34880          * through a corresponding {@link Ext.data.Store}.{@link Ext.data.Store#exception exception},\r
34881          * so any Store instance may observe this event.</p>\r
34882          * <p>In addition to being fired through the DataProxy instance that raised the event, this event is also fired\r
34883          * through the Ext.data.DataProxy <i>class</i> to allow for centralized processing of exception events from <b>all</b>\r
34884          * DataProxies by attaching a listener to the Ext.data.Proxy class itself.</p>\r
34885          * <p>This event can be fired for one of two reasons:</p>\r
34886          * <div class="mdetail-params"><ul>\r
34887          * <li>remote-request <b>failed</b> : <div class="sub-desc">\r
34888          * The server did not return status === 200.\r
34889          * </div></li>\r
34890          * <li>remote-request <b>succeeded</b> : <div class="sub-desc">\r
34891          * The remote-request succeeded but the reader could not read the response.\r
34892          * This means the server returned data, but the configured Reader threw an\r
34893          * error while reading the response.  In this case, this event will be\r
34894          * raised and the caught error will be passed along into this event.\r
34895          * </div></li>\r
34896          * </ul></div>\r
34897          * <br><p>This event fires with two different contexts based upon the 2nd\r
34898          * parameter <tt>type [remote|response]</tt>.  The first four parameters\r
34899          * are identical between the two contexts -- only the final two parameters\r
34900          * differ.</p>\r
34901          * @param {DataProxy} this The proxy that sent the request\r
34902          * @param {String} type\r
34903          * <p>The value of this parameter will be either <tt>'response'</tt> or <tt>'remote'</tt>.</p>\r
34904          * <div class="mdetail-params"><ul>\r
34905          * <li><b><tt>'response'</tt></b> : <div class="sub-desc">\r
34906          * <p>An <b>invalid</b> response from the server was returned: either 404,\r
34907          * 500 or the response meta-data does not match that defined in the DataReader\r
34908          * (e.g.: root, idProperty, successProperty).</p>\r
34909          * </div></li>\r
34910          * <li><b><tt>'remote'</tt></b> : <div class="sub-desc">\r
34911          * <p>A <b>valid</b> response was returned from the server having\r
34912          * successProperty === false.  This response might contain an error-message\r
34913          * sent from the server.  For example, the user may have failed\r
34914          * authentication/authorization or a database validation error occurred.</p>\r
34915          * </div></li>\r
34916          * </ul></div>\r
34917          * @param {String} action Name of the action (see {@link Ext.data.Api#actions}.\r
34918          * @param {Object} options The options for the action that were specified in the {@link #request}.\r
34919          * @param {Object} response\r
34920          * <p>The value of this parameter depends on the value of the <code>type</code> parameter:</p>\r
34921          * <div class="mdetail-params"><ul>\r
34922          * <li><b><tt>'response'</tt></b> : <div class="sub-desc">\r
34923          * <p>The raw browser response object (e.g.: XMLHttpRequest)</p>\r
34924          * </div></li>\r
34925          * <li><b><tt>'remote'</tt></b> : <div class="sub-desc">\r
34926          * <p>The decoded response object sent from the server.</p>\r
34927          * </div></li>\r
34928          * </ul></div>\r
34929          * @param {Mixed} arg\r
34930          * <p>The type and value of this parameter depends on the value of the <code>type</code> parameter:</p>\r
34931          * <div class="mdetail-params"><ul>\r
34932          * <li><b><tt>'response'</tt></b> : Error<div class="sub-desc">\r
34933          * <p>The JavaScript Error object caught if the configured Reader could not read the data.\r
34934          * If the remote request returns success===false, this parameter will be null.</p>\r
34935          * </div></li>\r
34936          * <li><b><tt>'remote'</tt></b> : Record/Record[]<div class="sub-desc">\r
34937          * <p>This parameter will only exist if the <tt>action</tt> was a <b>write</b> action\r
34938          * (Ext.data.Api.actions.create|update|destroy).</p>\r
34939          * </div></li>\r
34940          * </ul></div>\r
34941          */\r
34942         'exception',\r
34943         /**\r
34944          * @event beforeload\r
34945          * Fires before a request to retrieve a data object.\r
34946          * @param {DataProxy} this The proxy for the request\r
34947          * @param {Object} params The params object passed to the {@link #request} function\r
34948          */\r
34949         'beforeload',\r
34950         /**\r
34951          * @event load\r
34952          * Fires before the load method's callback is called.\r
34953          * @param {DataProxy} this The proxy for the request\r
34954          * @param {Object} o The request transaction object\r
34955          * @param {Object} options The callback's <tt>options</tt> property as passed to the {@link #request} function\r
34956          */\r
34957         'load',\r
34958         /**\r
34959          * @event loadexception\r
34960          * <p>This event is <b>deprecated</b>.  The signature of the loadexception event\r
34961          * varies depending on the proxy, use the catch-all {@link #exception} event instead.\r
34962          * This event will fire in addition to the {@link #exception} event.</p>\r
34963          * @param {misc} misc See {@link #exception}.\r
34964          * @deprecated\r
34965          */\r
34966         'loadexception',\r
34967         /**\r
34968          * @event beforewrite\r
34969          * <p>Fires before a request is generated for one of the actions Ext.data.Api.actions.create|update|destroy</p>\r
34970          * <p>In addition to being fired through the DataProxy instance that raised the event, this event is also fired\r
34971          * through the Ext.data.DataProxy <i>class</i> to allow for centralized processing of beforewrite events from <b>all</b>\r
34972          * DataProxies by attaching a listener to the Ext.data.Proxy class itself.</p>\r
34973          * @param {DataProxy} this The proxy for the request\r
34974          * @param {String} action [Ext.data.Api.actions.create|update|destroy]\r
34975          * @param {Record/Array[Record]} rs The Record(s) to create|update|destroy.\r
34976          * @param {Object} params The request <code>params</code> object.  Edit <code>params</code> to add parameters to the request.\r
34977          */\r
34978         'beforewrite',\r
34979         /**\r
34980          * @event write\r
34981          * <p>Fires before the request-callback is called</p>\r
34982          * <p>In addition to being fired through the DataProxy instance that raised the event, this event is also fired\r
34983          * through the Ext.data.DataProxy <i>class</i> to allow for centralized processing of write events from <b>all</b>\r
34984          * DataProxies by attaching a listener to the Ext.data.Proxy class itself.</p>\r
34985          * @param {DataProxy} this The proxy that sent the request\r
34986          * @param {String} action [Ext.data.Api.actions.create|upate|destroy]\r
34987          * @param {Object} data The data object extracted from the server-response\r
34988          * @param {Object} response The decoded response from server\r
34989          * @param {Record/Record{}} rs The records from Store\r
34990          * @param {Object} options The callback's <tt>options</tt> property as passed to the {@link #request} function\r
34991          */\r
34992         'write'\r
34993     );\r
34994     Ext.data.DataProxy.superclass.constructor.call(this);\r
34995 \r
34996     // Prepare the proxy api.  Ensures all API-actions are defined with the Object-form.\r
34997     try {\r
34998         Ext.data.Api.prepare(this);\r
34999     } catch (e) {\r
35000         if (e instanceof Ext.data.Api.Error) {\r
35001             e.toConsole();\r
35002         }\r
35003     }\r
35004     // relay each proxy's events onto Ext.data.DataProxy class for centralized Proxy-listening\r
35005     Ext.data.DataProxy.relayEvents(this, ['beforewrite', 'write', 'exception']);\r
35006 };\r
35007 \r
35008 Ext.extend(Ext.data.DataProxy, Ext.util.Observable, {\r
35009     /**\r
35010      * @cfg {Boolean} restful\r
35011      * <p>Defaults to <tt>false</tt>.  Set to <tt>true</tt> to operate in a RESTful manner.</p>\r
35012      * <br><p> Note: this parameter will automatically be set to <tt>true</tt> if the\r
35013      * {@link Ext.data.Store} it is plugged into is set to <code>restful: true</code>. If the\r
35014      * Store is RESTful, there is no need to set this option on the proxy.</p>\r
35015      * <br><p>RESTful implementations enable the serverside framework to automatically route\r
35016      * actions sent to one url based upon the HTTP method, for example:\r
35017      * <pre><code>\r
35018 store: new Ext.data.Store({\r
35019     restful: true,\r
35020     proxy: new Ext.data.HttpProxy({url:'/users'}); // all requests sent to /users\r
35021     ...\r
35022 )}\r
35023      * </code></pre>\r
35024      * If there is no <code>{@link #api}</code> specified in the configuration of the proxy,\r
35025      * all requests will be marshalled to a single RESTful url (/users) so the serverside\r
35026      * framework can inspect the HTTP Method and act accordingly:\r
35027      * <pre>\r
35028 <u>Method</u>   <u>url</u>        <u>action</u>\r
35029 POST     /users     create\r
35030 GET      /users     read\r
35031 PUT      /users/23  update\r
35032 DESTROY  /users/23  delete\r
35033      * </pre></p>\r
35034      * <p>If set to <tt>true</tt>, a {@link Ext.data.Record#phantom non-phantom} record's\r
35035      * {@link Ext.data.Record#id id} will be appended to the url. Some MVC (e.g., Ruby on Rails,\r
35036      * Merb and Django) support segment based urls where the segments in the URL follow the\r
35037      * Model-View-Controller approach:<pre><code>\r
35038      * someSite.com/controller/action/id\r
35039      * </code></pre>\r
35040      * Where the segments in the url are typically:<div class="mdetail-params"><ul>\r
35041      * <li>The first segment : represents the controller class that should be invoked.</li>\r
35042      * <li>The second segment : represents the class function, or method, that should be called.</li>\r
35043      * <li>The third segment : represents the ID (a variable typically passed to the method).</li>\r
35044      * </ul></div></p>\r
35045      * <br><p>Refer to <code>{@link Ext.data.DataProxy#api}</code> for additional information.</p>\r
35046      */\r
35047     restful: false,\r
35048 \r
35049     /**\r
35050      * <p>Redefines the Proxy's API or a single action of an API. Can be called with two method signatures.</p>\r
35051      * <p>If called with an object as the only parameter, the object should redefine the <b>entire</b> API, e.g.:</p><pre><code>\r
35052 proxy.setApi({\r
35053     read    : '/users/read',\r
35054     create  : '/users/create',\r
35055     update  : '/users/update',\r
35056     destroy : '/users/destroy'\r
35057 });\r
35058 </code></pre>\r
35059      * <p>If called with two parameters, the first parameter should be a string specifying the API action to\r
35060      * redefine and the second parameter should be the URL (or function if using DirectProxy) to call for that action, e.g.:</p><pre><code>\r
35061 proxy.setApi(Ext.data.Api.actions.read, '/users/new_load_url');\r
35062 </code></pre>\r
35063      * @param {String/Object} api An API specification object, or the name of an action.\r
35064      * @param {String/Function} url The URL (or function if using DirectProxy) to call for the action.\r
35065      */\r
35066     setApi : function() {\r
35067         if (arguments.length == 1) {\r
35068             var valid = Ext.data.Api.isValid(arguments[0]);\r
35069             if (valid === true) {\r
35070                 this.api = arguments[0];\r
35071             }\r
35072             else {\r
35073                 throw new Ext.data.Api.Error('invalid', valid);\r
35074             }\r
35075         }\r
35076         else if (arguments.length == 2) {\r
35077             if (!Ext.data.Api.isAction(arguments[0])) {\r
35078                 throw new Ext.data.Api.Error('invalid', arguments[0]);\r
35079             }\r
35080             this.api[arguments[0]] = arguments[1];\r
35081         }\r
35082         Ext.data.Api.prepare(this);\r
35083     },\r
35084 \r
35085     /**\r
35086      * Returns true if the specified action is defined as a unique action in the api-config.\r
35087      * request.  If all API-actions are routed to unique urls, the xaction parameter is unecessary.  However, if no api is defined\r
35088      * and all Proxy actions are routed to DataProxy#url, the server-side will require the xaction parameter to perform a switch to\r
35089      * the corresponding code for CRUD action.\r
35090      * @param {String [Ext.data.Api.CREATE|READ|UPDATE|DESTROY]} action\r
35091      * @return {Boolean}\r
35092      */\r
35093     isApiAction : function(action) {\r
35094         return (this.api[action]) ? true : false;\r
35095     },\r
35096 \r
35097     /**\r
35098      * All proxy actions are executed through this method.  Automatically fires the "before" + action event\r
35099      * @param {String} action Name of the action\r
35100      * @param {Ext.data.Record/Ext.data.Record[]/null} rs Will be null when action is 'load'\r
35101      * @param {Object} params\r
35102      * @param {Ext.data.DataReader} reader\r
35103      * @param {Function} callback\r
35104      * @param {Object} scope The scope (<code>this</code> reference) in which the callback function is executed. Defaults to the Proxy object.\r
35105      * @param {Object} options Any options specified for the action (e.g. see {@link Ext.data.Store#load}.\r
35106      */\r
35107     request : function(action, rs, params, reader, callback, scope, options) {\r
35108         if (!this.api[action] && !this.load) {\r
35109             throw new Ext.data.DataProxy.Error('action-undefined', action);\r
35110         }\r
35111         params = params || {};\r
35112         if ((action === Ext.data.Api.actions.read) ? this.fireEvent("beforeload", this, params) : this.fireEvent("beforewrite", this, action, rs, params) !== false) {\r
35113             this.doRequest.apply(this, arguments);\r
35114         }\r
35115         else {\r
35116             callback.call(scope || this, null, options, false);\r
35117         }\r
35118     },\r
35119 \r
35120 \r
35121     /**\r
35122      * <b>Deprecated</b> load method using old method signature. See {@doRequest} for preferred method.\r
35123      * @deprecated\r
35124      * @param {Object} params\r
35125      * @param {Object} reader\r
35126      * @param {Object} callback\r
35127      * @param {Object} scope\r
35128      * @param {Object} arg\r
35129      */\r
35130     load : null,\r
35131 \r
35132     /**\r
35133      * @cfg {Function} doRequest Abstract method that should be implemented in all subclasses.  <b>Note:</b> Should only be used by custom-proxy developers.\r
35134      * (e.g.: {@link Ext.data.HttpProxy#doRequest HttpProxy.doRequest},\r
35135      * {@link Ext.data.DirectProxy#doRequest DirectProxy.doRequest}).\r
35136      */\r
35137     doRequest : function(action, rs, params, reader, callback, scope, options) {\r
35138         // default implementation of doRequest for backwards compatibility with 2.0 proxies.\r
35139         // If we're executing here, the action is probably "load".\r
35140         // Call with the pre-3.0 method signature.\r
35141         this.load(params, reader, callback, scope, options);\r
35142     },\r
35143 \r
35144     /**\r
35145      * @cfg {Function} onRead Abstract method that should be implemented in all subclasses.  <b>Note:</b> Should only be used by custom-proxy developers.  Callback for read {@link Ext.data.Api#actions action}.\r
35146      * @param {String} action Action name as per {@link Ext.data.Api.actions#read}.\r
35147      * @param {Object} o The request transaction object\r
35148      * @param {Object} res The server response\r
35149      * @fires loadexception (deprecated)\r
35150      * @fires exception\r
35151      * @fires load\r
35152      * @protected\r
35153      */\r
35154     onRead : Ext.emptyFn,\r
35155     /**\r
35156      * @cfg {Function} onWrite Abstract method that should be implemented in all subclasses.  <b>Note:</b> Should only be used by custom-proxy developers.  Callback for <i>create, update and destroy</i> {@link Ext.data.Api#actions actions}.\r
35157      * @param {String} action [Ext.data.Api.actions.create|read|update|destroy]\r
35158      * @param {Object} trans The request transaction object\r
35159      * @param {Object} res The server response\r
35160      * @fires exception\r
35161      * @fires write\r
35162      * @protected\r
35163      */\r
35164     onWrite : Ext.emptyFn,\r
35165     /**\r
35166      * buildUrl\r
35167      * Sets the appropriate url based upon the action being executed.  If restful is true, and only a single record is being acted upon,\r
35168      * url will be built Rails-style, as in "/controller/action/32".  restful will aply iff the supplied record is an\r
35169      * instance of Ext.data.Record rather than an Array of them.\r
35170      * @param {String} action The api action being executed [read|create|update|destroy]\r
35171      * @param {Ext.data.Record/Array[Ext.data.Record]} The record or Array of Records being acted upon.\r
35172      * @return {String} url\r
35173      * @private\r
35174      */\r
35175     buildUrl : function(action, record) {\r
35176         record = record || null;\r
35177 \r
35178         // conn.url gets nullified after each request.  If it's NOT null here, that means the user must have intervened with a call\r
35179         // to DataProxy#setUrl or DataProxy#setApi and changed it before the request was executed.  If that's the case, use conn.url,\r
35180         // otherwise, build the url from the api or this.url.\r
35181         var url = (this.conn && this.conn.url) ? this.conn.url : (this.api[action]) ? this.api[action].url : this.url;\r
35182         if (!url) {\r
35183             throw new Ext.data.Api.Error('invalid-url', action);\r
35184         }\r
35185 \r
35186         // look for urls having "provides" suffix used in some MVC frameworks like Rails/Merb and others.  The provides suffice informs\r
35187         // the server what data-format the client is dealing with and returns data in the same format (eg: application/json, application/xml, etc)\r
35188         // e.g.: /users.json, /users.xml, etc.\r
35189         // with restful routes, we need urls like:\r
35190         // PUT /users/1.json\r
35191         // DELETE /users/1.json\r
35192         var provides = null;\r
35193         var m = url.match(/(.*)(\.json|\.xml|\.html)$/);\r
35194         if (m) {\r
35195             provides = m[2];    // eg ".json"\r
35196             url      = m[1];    // eg: "/users"\r
35197         }\r
35198         // prettyUrls is deprectated in favor of restful-config\r
35199         if ((this.restful === true || this.prettyUrls === true) && record instanceof Ext.data.Record && !record.phantom) {\r
35200             url += '/' + record.id;\r
35201         }\r
35202         return (provides === null) ? url : url + provides;\r
35203     },\r
35204 \r
35205     /**\r
35206      * Destroys the proxy by purging any event listeners and cancelling any active requests.\r
35207      */\r
35208     destroy: function(){\r
35209         this.purgeListeners();\r
35210     }\r
35211 });\r
35212 \r
35213 // Apply the Observable prototype to the DataProxy class so that proxy instances can relay their\r
35214 // events to the class.  Allows for centralized listening of all proxy instances upon the DataProxy class.\r
35215 Ext.apply(Ext.data.DataProxy, Ext.util.Observable.prototype);\r
35216 Ext.util.Observable.call(Ext.data.DataProxy);\r
35217 \r
35218 /**\r
35219  * @class Ext.data.DataProxy.Error\r
35220  * @extends Ext.Error\r
35221  * DataProxy Error extension.\r
35222  * constructor\r
35223  * @param {String} name\r
35224  * @param {Record/Array[Record]/Array}\r
35225  */\r
35226 Ext.data.DataProxy.Error = Ext.extend(Ext.Error, {\r
35227     constructor : function(message, arg) {\r
35228         this.arg = arg;\r
35229         Ext.Error.call(this, message);\r
35230     },\r
35231     name: 'Ext.data.DataProxy'\r
35232 });\r
35233 Ext.apply(Ext.data.DataProxy.Error.prototype, {\r
35234     lang: {\r
35235         'action-undefined': "DataProxy attempted to execute an API-action but found an undefined url / function.  Please review your Proxy url/api-configuration.",\r
35236         'api-invalid': 'Recieved an invalid API-configuration.  Please ensure your proxy API-configuration contains only the actions from Ext.data.Api.actions.'\r
35237     }\r
35238 });\r
35239 \r
35240 \r
35241 /**
35242  * @class Ext.data.Request
35243  * A simple Request class used internally to the data package to provide more generalized remote-requests
35244  * to a DataProxy.
35245  * TODO Not yet implemented.  Implement in Ext.data.Store#execute
35246  */
35247 Ext.data.Request = function(params) {
35248     Ext.apply(this, params);
35249 };
35250 Ext.data.Request.prototype = {
35251     /**
35252      * @cfg {String} action
35253      */
35254     action : undefined,
35255     /**
35256      * @cfg {Ext.data.Record[]/Ext.data.Record} rs The Store recordset associated with the request.
35257      */
35258     rs : undefined,
35259     /**
35260      * @cfg {Object} params HTTP request params
35261      */
35262     params: undefined,
35263     /**
35264      * @cfg {Function} callback The function to call when request is complete
35265      */
35266     callback : Ext.emptyFn,
35267     /**
35268      * @cfg {Object} scope The scope of the callback funtion
35269      */
35270     scope : undefined,
35271     /**
35272      * @cfg {Ext.data.DataReader} reader The DataReader instance which will parse the received response
35273      */
35274     reader : undefined
35275 };
35276 /**
35277  * @class Ext.data.Response
35278  * A generic response class to normalize response-handling internally to the framework.
35279  */
35280 Ext.data.Response = function(params) {
35281     Ext.apply(this, params);
35282 };
35283 Ext.data.Response.prototype = {
35284     /**
35285      * @cfg {String} action {@link Ext.data.Api#actions}
35286      */
35287     action: undefined,
35288     /**
35289      * @cfg {Boolean} success
35290      */
35291     success : undefined,
35292     /**
35293      * @cfg {String} message
35294      */
35295     message : undefined,
35296     /**
35297      * @cfg {Array/Object} data
35298      */
35299     data: undefined,
35300     /**
35301      * @cfg {Object} raw The raw response returned from server-code
35302      */
35303     raw: undefined,
35304     /**
35305      * @cfg {Ext.data.Record/Ext.data.Record[]} records related to the Request action
35306      */
35307     records: undefined
35308 };
35309 /**\r
35310  * @class Ext.data.ScriptTagProxy\r
35311  * @extends Ext.data.DataProxy\r
35312  * An implementation of Ext.data.DataProxy that reads a data object from a URL which may be in a domain\r
35313  * other than the originating domain of the running page.<br>\r
35314  * <p>\r
35315  * <b>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain\r
35316  * of the running page, you must use this class, rather than HttpProxy.</b><br>\r
35317  * <p>\r
35318  * The content passed back from a server resource requested by a ScriptTagProxy <b>must</b> be executable JavaScript\r
35319  * source code because it is used as the source inside a &lt;script> tag.<br>\r
35320  * <p>\r
35321  * In order for the browser to process the returned data, the server must wrap the data object\r
35322  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.\r
35323  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy\r
35324  * depending on whether the callback name was passed:\r
35325  * <p>\r
35326  * <pre><code>\r
35327 boolean scriptTag = false;\r
35328 String cb = request.getParameter("callback");\r
35329 if (cb != null) {\r
35330     scriptTag = true;\r
35331     response.setContentType("text/javascript");\r
35332 } else {\r
35333     response.setContentType("application/x-json");\r
35334 }\r
35335 Writer out = response.getWriter();\r
35336 if (scriptTag) {\r
35337     out.write(cb + "(");\r
35338 }\r
35339 out.print(dataBlock.toJsonString());\r
35340 if (scriptTag) {\r
35341     out.write(");");\r
35342 }\r
35343 </code></pre>\r
35344  * <p>Below is a PHP example to do the same thing:</p><pre><code>\r
35345 $callback = $_REQUEST['callback'];\r
35346 \r
35347 // Create the output object.\r
35348 $output = array('a' => 'Apple', 'b' => 'Banana');\r
35349 \r
35350 //start output\r
35351 if ($callback) {\r
35352     header('Content-Type: text/javascript');\r
35353     echo $callback . '(' . json_encode($output) . ');';\r
35354 } else {\r
35355     header('Content-Type: application/x-json');\r
35356     echo json_encode($output);\r
35357 }\r
35358 </code></pre>\r
35359  * <p>Below is the ASP.Net code to do the same thing:</p><pre><code>\r
35360 String jsonString = "{success: true}";\r
35361 String cb = Request.Params.Get("callback");\r
35362 String responseString = "";\r
35363 if (!String.IsNullOrEmpty(cb)) {\r
35364     responseString = cb + "(" + jsonString + ")";\r
35365 } else {\r
35366     responseString = jsonString;\r
35367 }\r
35368 Response.Write(responseString);\r
35369 </code></pre>\r
35370  *\r
35371  * @constructor\r
35372  * @param {Object} config A configuration object.\r
35373  */\r
35374 Ext.data.ScriptTagProxy = function(config){\r
35375     Ext.apply(this, config);\r
35376 \r
35377     Ext.data.ScriptTagProxy.superclass.constructor.call(this, config);\r
35378 \r
35379     this.head = document.getElementsByTagName("head")[0];\r
35380 \r
35381     /**\r
35382      * @event loadexception\r
35383      * <b>Deprecated</b> in favor of 'exception' event.\r
35384      * Fires if an exception occurs in the Proxy during data loading.  This event can be fired for one of two reasons:\r
35385      * <ul><li><b>The load call timed out.</b>  This means the load callback did not execute within the time limit\r
35386      * specified by {@link #timeout}.  In this case, this event will be raised and the\r
35387      * fourth parameter (read error) will be null.</li>\r
35388      * <li><b>The load succeeded but the reader could not read the response.</b>  This means the server returned\r
35389      * data, but the configured Reader threw an error while reading the data.  In this case, this event will be\r
35390      * raised and the caught error will be passed along as the fourth parameter of this event.</li></ul>\r
35391      * Note that this event is also relayed through {@link Ext.data.Store}, so you can listen for it directly\r
35392      * on any Store instance.\r
35393      * @param {Object} this\r
35394      * @param {Object} options The loading options that were specified (see {@link #load} for details).  If the load\r
35395      * call timed out, this parameter will be null.\r
35396      * @param {Object} arg The callback's arg object passed to the {@link #load} function\r
35397      * @param {Error} e The JavaScript Error object caught if the configured Reader could not read the data.\r
35398      * If the remote request returns success: false, this parameter will be null.\r
35399      */\r
35400 };\r
35401 \r
35402 Ext.data.ScriptTagProxy.TRANS_ID = 1000;\r
35403 \r
35404 Ext.extend(Ext.data.ScriptTagProxy, Ext.data.DataProxy, {\r
35405     /**\r
35406      * @cfg {String} url The URL from which to request the data object.\r
35407      */\r
35408     /**\r
35409      * @cfg {Number} timeout (optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.\r
35410      */\r
35411     timeout : 30000,\r
35412     /**\r
35413      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells\r
35414      * the server the name of the callback function set up by the load call to process the returned data object.\r
35415      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate\r
35416      * javascript output which calls this named function passing the data object as its only parameter.\r
35417      */\r
35418     callbackParam : "callback",\r
35419     /**\r
35420      *  @cfg {Boolean} nocache (optional) Defaults to true. Disable caching by adding a unique parameter\r
35421      * name to the request.\r
35422      */\r
35423     nocache : true,\r
35424 \r
35425     /**\r
35426      * HttpProxy implementation of DataProxy#doRequest\r
35427      * @param {String} action\r
35428      * @param {Ext.data.Record/Ext.data.Record[]} rs If action is <tt>read</tt>, rs will be null\r
35429      * @param {Object} params An object containing properties which are to be used as HTTP parameters\r
35430      * for the request to the remote server.\r
35431      * @param {Ext.data.DataReader} reader The Reader object which converts the data\r
35432      * object into a block of Ext.data.Records.\r
35433      * @param {Function} callback The function into which to pass the block of Ext.data.Records.\r
35434      * The function must be passed <ul>\r
35435      * <li>The Record block object</li>\r
35436      * <li>The "arg" argument from the load function</li>\r
35437      * <li>A boolean success indicator</li>\r
35438      * </ul>\r
35439      * @param {Object} scope The scope (<code>this</code> reference) in which the callback function is executed. Defaults to the browser window.\r
35440      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.\r
35441      */\r
35442     doRequest : function(action, rs, params, reader, callback, scope, arg) {\r
35443         var p = Ext.urlEncode(Ext.apply(params, this.extraParams));\r
35444 \r
35445         var url = this.buildUrl(action, rs);\r
35446         if (!url) {\r
35447             throw new Ext.data.Api.Error('invalid-url', url);\r
35448         }\r
35449         url = Ext.urlAppend(url, p);\r
35450 \r
35451         if(this.nocache){\r
35452             url = Ext.urlAppend(url, '_dc=' + (new Date().getTime()));\r
35453         }\r
35454         var transId = ++Ext.data.ScriptTagProxy.TRANS_ID;\r
35455         var trans = {\r
35456             id : transId,\r
35457             action: action,\r
35458             cb : "stcCallback"+transId,\r
35459             scriptId : "stcScript"+transId,\r
35460             params : params,\r
35461             arg : arg,\r
35462             url : url,\r
35463             callback : callback,\r
35464             scope : scope,\r
35465             reader : reader\r
35466         };\r
35467         window[trans.cb] = this.createCallback(action, rs, trans);\r
35468         url += String.format("&{0}={1}", this.callbackParam, trans.cb);\r
35469         if(this.autoAbort !== false){\r
35470             this.abort();\r
35471         }\r
35472 \r
35473         trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);\r
35474 \r
35475         var script = document.createElement("script");\r
35476         script.setAttribute("src", url);\r
35477         script.setAttribute("type", "text/javascript");\r
35478         script.setAttribute("id", trans.scriptId);\r
35479         this.head.appendChild(script);\r
35480 \r
35481         this.trans = trans;\r
35482     },\r
35483 \r
35484     // @private createCallback\r
35485     createCallback : function(action, rs, trans) {\r
35486         var self = this;\r
35487         return function(res) {\r
35488             self.trans = false;\r
35489             self.destroyTrans(trans, true);\r
35490             if (action === Ext.data.Api.actions.read) {\r
35491                 self.onRead.call(self, action, trans, res);\r
35492             } else {\r
35493                 self.onWrite.call(self, action, trans, res, rs);\r
35494             }\r
35495         };\r
35496     },\r
35497     /**\r
35498      * Callback for read actions\r
35499      * @param {String} action [Ext.data.Api.actions.create|read|update|destroy]\r
35500      * @param {Object} trans The request transaction object\r
35501      * @param {Object} res The server response\r
35502      * @protected\r
35503      */\r
35504     onRead : function(action, trans, res) {\r
35505         var result;\r
35506         try {\r
35507             result = trans.reader.readRecords(res);\r
35508         }catch(e){\r
35509             // @deprecated: fire loadexception\r
35510             this.fireEvent("loadexception", this, trans, res, e);\r
35511 \r
35512             this.fireEvent('exception', this, 'response', action, trans, res, e);\r
35513             trans.callback.call(trans.scope||window, null, trans.arg, false);\r
35514             return;\r
35515         }\r
35516         if (result.success === false) {\r
35517             // @deprecated: fire old loadexception for backwards-compat.\r
35518             this.fireEvent('loadexception', this, trans, res);\r
35519 \r
35520             this.fireEvent('exception', this, 'remote', action, trans, res, null);\r
35521         } else {\r
35522             this.fireEvent("load", this, res, trans.arg);\r
35523         }\r
35524         trans.callback.call(trans.scope||window, result, trans.arg, result.success);\r
35525     },\r
35526     /**\r
35527      * Callback for write actions\r
35528      * @param {String} action [Ext.data.Api.actions.create|read|update|destroy]\r
35529      * @param {Object} trans The request transaction object\r
35530      * @param {Object} res The server response\r
35531      * @protected\r
35532      */\r
35533     onWrite : function(action, trans, response, rs) {\r
35534         var reader = trans.reader;\r
35535         try {\r
35536             // though we already have a response object here in STP, run through readResponse to catch any meta-data exceptions.\r
35537             var res = reader.readResponse(action, response);\r
35538         } catch (e) {\r
35539             this.fireEvent('exception', this, 'response', action, trans, res, e);\r
35540             trans.callback.call(trans.scope||window, null, res, false);\r
35541             return;\r
35542         }\r
35543         if(!res.success === true){\r
35544             this.fireEvent('exception', this, 'remote', action, trans, res, rs);\r
35545             trans.callback.call(trans.scope||window, null, res, false);\r
35546             return;\r
35547         }\r
35548         this.fireEvent("write", this, action, res.data, res, rs, trans.arg );\r
35549         trans.callback.call(trans.scope||window, res.data, res, true);\r
35550     },\r
35551 \r
35552     // private\r
35553     isLoading : function(){\r
35554         return this.trans ? true : false;\r
35555     },\r
35556 \r
35557     /**\r
35558      * Abort the current server request.\r
35559      */\r
35560     abort : function(){\r
35561         if(this.isLoading()){\r
35562             this.destroyTrans(this.trans);\r
35563         }\r
35564     },\r
35565 \r
35566     // private\r
35567     destroyTrans : function(trans, isLoaded){\r
35568         this.head.removeChild(document.getElementById(trans.scriptId));\r
35569         clearTimeout(trans.timeoutId);\r
35570         if(isLoaded){\r
35571             window[trans.cb] = undefined;\r
35572             try{\r
35573                 delete window[trans.cb];\r
35574             }catch(e){}\r
35575         }else{\r
35576             // if hasn't been loaded, wait for load to remove it to prevent script error\r
35577             window[trans.cb] = function(){\r
35578                 window[trans.cb] = undefined;\r
35579                 try{\r
35580                     delete window[trans.cb];\r
35581                 }catch(e){}\r
35582             };\r
35583         }\r
35584     },\r
35585 \r
35586     // private\r
35587     handleFailure : function(trans){\r
35588         this.trans = false;\r
35589         this.destroyTrans(trans, false);\r
35590         if (trans.action === Ext.data.Api.actions.read) {\r
35591             // @deprecated firing loadexception\r
35592             this.fireEvent("loadexception", this, null, trans.arg);\r
35593         }\r
35594 \r
35595         this.fireEvent('exception', this, 'response', trans.action, {\r
35596             response: null,\r
35597             options: trans.arg\r
35598         });\r
35599         trans.callback.call(trans.scope||window, null, trans.arg, false);\r
35600     },\r
35601 \r
35602     // inherit docs\r
35603     destroy: function(){\r
35604         this.abort();\r
35605         Ext.data.ScriptTagProxy.superclass.destroy.call(this);\r
35606     }\r
35607 });/**\r
35608  * @class Ext.data.HttpProxy\r
35609  * @extends Ext.data.DataProxy\r
35610  * <p>An implementation of {@link Ext.data.DataProxy} that processes data requests within the same\r
35611  * domain of the originating page.</p>\r
35612  * <p><b>Note</b>: this class cannot be used to retrieve data from a domain other\r
35613  * than the domain from which the running page was served. For cross-domain requests, use a\r
35614  * {@link Ext.data.ScriptTagProxy ScriptTagProxy}.</p>\r
35615  * <p>Be aware that to enable the browser to parse an XML document, the server must set\r
35616  * the Content-Type header in the HTTP response to "<tt>text/xml</tt>".</p>\r
35617  * @constructor\r
35618  * @param {Object} conn\r
35619  * An {@link Ext.data.Connection} object, or options parameter to {@link Ext.Ajax#request}.\r
35620  * <p>Note that if this HttpProxy is being used by a {@link Ext.data.Store Store}, then the\r
35621  * Store's call to {@link #load} will override any specified <tt>callback</tt> and <tt>params</tt>\r
35622  * options. In this case, use the Store's {@link Ext.data.Store#events events} to modify parameters,\r
35623  * or react to loading events. The Store's {@link Ext.data.Store#baseParams baseParams} may also be\r
35624  * used to pass parameters known at instantiation time.</p>\r
35625  * <p>If an options parameter is passed, the singleton {@link Ext.Ajax} object will be used to make\r
35626  * the request.</p>\r
35627  */\r
35628 Ext.data.HttpProxy = function(conn){\r
35629     Ext.data.HttpProxy.superclass.constructor.call(this, conn);\r
35630 \r
35631     /**\r
35632      * The Connection object (Or options parameter to {@link Ext.Ajax#request}) which this HttpProxy\r
35633      * uses to make requests to the server. Properties of this object may be changed dynamically to\r
35634      * change the way data is requested.\r
35635      * @property\r
35636      */\r
35637     this.conn = conn;\r
35638 \r
35639     // nullify the connection url.  The url param has been copied to 'this' above.  The connection\r
35640     // url will be set during each execution of doRequest when buildUrl is called.  This makes it easier for users to override the\r
35641     // connection url during beforeaction events (ie: beforeload, beforewrite, etc).\r
35642     // Url is always re-defined during doRequest.\r
35643     this.conn.url = null;\r
35644 \r
35645     this.useAjax = !conn || !conn.events;\r
35646 \r
35647     // A hash containing active requests, keyed on action [Ext.data.Api.actions.create|read|update|destroy]\r
35648     var actions = Ext.data.Api.actions;\r
35649     this.activeRequest = {};\r
35650     for (var verb in actions) {\r
35651         this.activeRequest[actions[verb]] = undefined;\r
35652     }\r
35653 };\r
35654 \r
35655 Ext.extend(Ext.data.HttpProxy, Ext.data.DataProxy, {\r
35656     /**\r
35657      * Return the {@link Ext.data.Connection} object being used by this Proxy.\r
35658      * @return {Connection} The Connection object. This object may be used to subscribe to events on\r
35659      * a finer-grained basis than the DataProxy events.\r
35660      */\r
35661     getConnection : function() {\r
35662         return this.useAjax ? Ext.Ajax : this.conn;\r
35663     },\r
35664 \r
35665     /**\r
35666      * Used for overriding the url used for a single request.  Designed to be called during a beforeaction event.  Calling setUrl\r
35667      * will override any urls set via the api configuration parameter.  Set the optional parameter makePermanent to set the url for\r
35668      * all subsequent requests.  If not set to makePermanent, the next request will use the same url or api configuration defined\r
35669      * in the initial proxy configuration.\r
35670      * @param {String} url\r
35671      * @param {Boolean} makePermanent (Optional) [false]\r
35672      *\r
35673      * (e.g.: beforeload, beforesave, etc).\r
35674      */\r
35675     setUrl : function(url, makePermanent) {\r
35676         this.conn.url = url;\r
35677         if (makePermanent === true) {\r
35678             this.url = url;\r
35679             this.api = null;\r
35680             Ext.data.Api.prepare(this);\r
35681         }\r
35682     },\r
35683 \r
35684     /**\r
35685      * HttpProxy implementation of DataProxy#doRequest\r
35686      * @param {String} action The crud action type (create, read, update, destroy)\r
35687      * @param {Ext.data.Record/Ext.data.Record[]} rs If action is load, rs will be null\r
35688      * @param {Object} params An object containing properties which are to be used as HTTP parameters\r
35689      * for the request to the remote server.\r
35690      * @param {Ext.data.DataReader} reader The Reader object which converts the data\r
35691      * object into a block of Ext.data.Records.\r
35692      * @param {Function} callback\r
35693      * <div class="sub-desc"><p>A function to be called after the request.\r
35694      * The <tt>callback</tt> is passed the following arguments:<ul>\r
35695      * <li><tt>r</tt> : Ext.data.Record[] The block of Ext.data.Records.</li>\r
35696      * <li><tt>options</tt>: Options object from the action request</li>\r
35697      * <li><tt>success</tt>: Boolean success indicator</li></ul></p></div>\r
35698      * @param {Object} scope The scope (<code>this</code> reference) in which the callback function is executed. Defaults to the browser window.\r
35699      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.\r
35700      * @protected\r
35701      */\r
35702     doRequest : function(action, rs, params, reader, cb, scope, arg) {\r
35703         var  o = {\r
35704             method: (this.api[action]) ? this.api[action]['method'] : undefined,\r
35705             request: {\r
35706                 callback : cb,\r
35707                 scope : scope,\r
35708                 arg : arg\r
35709             },\r
35710             reader: reader,\r
35711             callback : this.createCallback(action, rs),\r
35712             scope: this\r
35713         };\r
35714 \r
35715         // If possible, transmit data using jsonData || xmlData on Ext.Ajax.request (An installed DataWriter would have written it there.).\r
35716         // Use std HTTP params otherwise.\r
35717         if (params.jsonData) {\r
35718             o.jsonData = params.jsonData;\r
35719         } else if (params.xmlData) {\r
35720             o.xmlData = params.xmlData;\r
35721         } else {\r
35722             o.params = params || {};\r
35723         }\r
35724         // Set the connection url.  If this.conn.url is not null here,\r
35725         // the user must have overridden the url during a beforewrite/beforeload event-handler.\r
35726         // this.conn.url is nullified after each request.\r
35727         this.conn.url = this.buildUrl(action, rs);\r
35728 \r
35729         if(this.useAjax){\r
35730 \r
35731             Ext.applyIf(o, this.conn);\r
35732 \r
35733             // If a currently running request is found for this action, abort it.\r
35734             if (this.activeRequest[action]) {\r
35735                 ////\r
35736                 // Disabled aborting activeRequest while implementing REST.  activeRequest[action] will have to become an array\r
35737                 // TODO ideas anyone?\r
35738                 //\r
35739                 //Ext.Ajax.abort(this.activeRequest[action]);\r
35740             }\r
35741             this.activeRequest[action] = Ext.Ajax.request(o);\r
35742         }else{\r
35743             this.conn.request(o);\r
35744         }\r
35745         // request is sent, nullify the connection url in preparation for the next request\r
35746         this.conn.url = null;\r
35747     },\r
35748 \r
35749     /**\r
35750      * Returns a callback function for a request.  Note a special case is made for the\r
35751      * read action vs all the others.\r
35752      * @param {String} action [create|update|delete|load]\r
35753      * @param {Ext.data.Record[]} rs The Store-recordset being acted upon\r
35754      * @private\r
35755      */\r
35756     createCallback : function(action, rs) {\r
35757         return function(o, success, response) {\r
35758             this.activeRequest[action] = undefined;\r
35759             if (!success) {\r
35760                 if (action === Ext.data.Api.actions.read) {\r
35761                     // @deprecated: fire loadexception for backwards compat.\r
35762                     // TODO remove in 3.1\r
35763                     this.fireEvent('loadexception', this, o, response);\r
35764                 }\r
35765                 this.fireEvent('exception', this, 'response', action, o, response);\r
35766                 o.request.callback.call(o.request.scope, null, o.request.arg, false);\r
35767                 return;\r
35768             }\r
35769             if (action === Ext.data.Api.actions.read) {\r
35770                 this.onRead(action, o, response);\r
35771             } else {\r
35772                 this.onWrite(action, o, response, rs);\r
35773             }\r
35774         };\r
35775     },\r
35776 \r
35777     /**\r
35778      * Callback for read action\r
35779      * @param {String} action Action name as per {@link Ext.data.Api.actions#read}.\r
35780      * @param {Object} o The request transaction object\r
35781      * @param {Object} res The server response\r
35782      * @fires loadexception (deprecated)\r
35783      * @fires exception\r
35784      * @fires load\r
35785      * @protected\r
35786      */\r
35787     onRead : function(action, o, response) {\r
35788         var result;\r
35789         try {\r
35790             result = o.reader.read(response);\r
35791         }catch(e){\r
35792             // @deprecated: fire old loadexception for backwards-compat.\r
35793             // TODO remove in 3.1\r
35794             this.fireEvent('loadexception', this, o, response, e);\r
35795 \r
35796             this.fireEvent('exception', this, 'response', action, o, response, e);\r
35797             o.request.callback.call(o.request.scope, null, o.request.arg, false);\r
35798             return;\r
35799         }\r
35800         if (result.success === false) {\r
35801             // @deprecated: fire old loadexception for backwards-compat.\r
35802             // TODO remove in 3.1\r
35803             this.fireEvent('loadexception', this, o, response);\r
35804 \r
35805             // Get DataReader read-back a response-object to pass along to exception event\r
35806             var res = o.reader.readResponse(action, response);\r
35807             this.fireEvent('exception', this, 'remote', action, o, res, null);\r
35808         }\r
35809         else {\r
35810             this.fireEvent('load', this, o, o.request.arg);\r
35811         }\r
35812         // TODO refactor onRead, onWrite to be more generalized now that we're dealing with Ext.data.Response instance\r
35813         // the calls to request.callback(...) in each will have to be made identical.\r
35814         // NOTE reader.readResponse does not currently return Ext.data.Response\r
35815         o.request.callback.call(o.request.scope, result, o.request.arg, result.success);\r
35816     },\r
35817     /**\r
35818      * Callback for write actions\r
35819      * @param {String} action [Ext.data.Api.actions.create|read|update|destroy]\r
35820      * @param {Object} trans The request transaction object\r
35821      * @param {Object} res The server response\r
35822      * @fires exception\r
35823      * @fires write\r
35824      * @protected\r
35825      */\r
35826     onWrite : function(action, o, response, rs) {\r
35827         var reader = o.reader;\r
35828         var res;\r
35829         try {\r
35830             res = reader.readResponse(action, response);\r
35831         } catch (e) {\r
35832             this.fireEvent('exception', this, 'response', action, o, response, e);\r
35833             o.request.callback.call(o.request.scope, null, o.request.arg, false);\r
35834             return;\r
35835         }\r
35836         if (res.success === true) {\r
35837             this.fireEvent('write', this, action, res.data, res, rs, o.request.arg);\r
35838         } else {\r
35839             this.fireEvent('exception', this, 'remote', action, o, res, rs);\r
35840         }\r
35841         // TODO refactor onRead, onWrite to be more generalized now that we're dealing with Ext.data.Response instance\r
35842         // the calls to request.callback(...) in each will have to be made similar.\r
35843         // NOTE reader.readResponse does not currently return Ext.data.Response\r
35844         o.request.callback.call(o.request.scope, res.data, res, res.success);\r
35845     },\r
35846 \r
35847     // inherit docs\r
35848     destroy: function(){\r
35849         if(!this.useAjax){\r
35850             this.conn.abort();\r
35851         }else if(this.activeRequest){\r
35852             var actions = Ext.data.Api.actions;\r
35853             for (var verb in actions) {\r
35854                 if(this.activeRequest[actions[verb]]){\r
35855                     Ext.Ajax.abort(this.activeRequest[actions[verb]]);\r
35856                 }\r
35857             }\r
35858         }\r
35859         Ext.data.HttpProxy.superclass.destroy.call(this);\r
35860     }\r
35861 });/**\r
35862  * @class Ext.data.MemoryProxy\r
35863  * @extends Ext.data.DataProxy\r
35864  * An implementation of Ext.data.DataProxy that simply passes the data specified in its constructor\r
35865  * to the Reader when its load method is called.\r
35866  * @constructor\r
35867  * @param {Object} data The data object which the Reader uses to construct a block of Ext.data.Records.\r
35868  */\r
35869 Ext.data.MemoryProxy = function(data){\r
35870     // Must define a dummy api with "read" action to satisfy DataProxy#doRequest and Ext.data.Api#prepare *before* calling super\r
35871     var api = {};\r
35872     api[Ext.data.Api.actions.read] = true;\r
35873     Ext.data.MemoryProxy.superclass.constructor.call(this, {\r
35874         api: api\r
35875     });\r
35876     this.data = data;\r
35877 };\r
35878 \r
35879 Ext.extend(Ext.data.MemoryProxy, Ext.data.DataProxy, {\r
35880     /**\r
35881      * @event loadexception\r
35882      * Fires if an exception occurs in the Proxy during data loading. Note that this event is also relayed\r
35883      * through {@link Ext.data.Store}, so you can listen for it directly on any Store instance.\r
35884      * @param {Object} this\r
35885      * @param {Object} arg The callback's arg object passed to the {@link #load} function\r
35886      * @param {Object} null This parameter does not apply and will always be null for MemoryProxy\r
35887      * @param {Error} e The JavaScript Error object caught if the configured Reader could not read the data\r
35888      */\r
35889 \r
35890        /**\r
35891      * MemoryProxy implementation of DataProxy#doRequest\r
35892      * @param {String} action\r
35893      * @param {Ext.data.Record/Ext.data.Record[]} rs If action is load, rs will be null\r
35894      * @param {Object} params An object containing properties which are to be used as HTTP parameters\r
35895      * for the request to the remote server.\r
35896      * @param {Ext.data.DataReader} reader The Reader object which converts the data\r
35897      * object into a block of Ext.data.Records.\r
35898      * @param {Function} callback The function into which to pass the block of Ext.data.Records.\r
35899      * The function must be passed <ul>\r
35900      * <li>The Record block object</li>\r
35901      * <li>The "arg" argument from the load function</li>\r
35902      * <li>A boolean success indicator</li>\r
35903      * </ul>\r
35904      * @param {Object} scope The scope (<code>this</code> reference) in which the callback function is executed. Defaults to the browser window.\r
35905      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.\r
35906      */\r
35907     doRequest : function(action, rs, params, reader, callback, scope, arg) {\r
35908         // No implementation for CRUD in MemoryProxy.  Assumes all actions are 'load'\r
35909         params = params || {};\r
35910         var result;\r
35911         try {\r
35912             result = reader.readRecords(this.data);\r
35913         }catch(e){\r
35914             // @deprecated loadexception\r
35915             this.fireEvent("loadexception", this, null, arg, e);\r
35916 \r
35917             this.fireEvent('exception', this, 'response', action, arg, null, e);\r
35918             callback.call(scope, null, arg, false);\r
35919             return;\r
35920         }\r
35921         callback.call(scope, result, arg, true);\r
35922     }\r
35923 });/**
35924  * @class Ext.data.JsonWriter
35925  * @extends Ext.data.DataWriter
35926  * DataWriter extension for writing an array or single {@link Ext.data.Record} object(s) in preparation for executing a remote CRUD action.
35927  */
35928 Ext.data.JsonWriter = Ext.extend(Ext.data.DataWriter, {
35929     /**
35930      * @cfg {Boolean} encode <tt>true</tt> to {@link Ext.util.JSON#encode encode} the
35931      * {@link Ext.data.DataWriter#toHash hashed data}. Defaults to <tt>true</tt>.  When using
35932      * {@link Ext.data.DirectProxy}, set this to <tt>false</tt> since Ext.Direct.JsonProvider will perform
35933      * its own json-encoding.  In addition, if you're using {@link Ext.data.HttpProxy}, setting to <tt>false</tt>
35934      * will cause HttpProxy to transmit data using the <b>jsonData</b> configuration-params of {@link Ext.Ajax#request}
35935      * instead of <b>params</b>.  When using a {@link Ext.data.Store#restful} Store, some serverside frameworks are
35936      * tuned to expect data through the jsonData mechanism.  In those cases, one will want to set <b>encode: <tt>false</tt></b>, as in
35937      * let the lower-level connection object (eg: Ext.Ajax) do the encoding.
35938      */
35939     encode : true,
35940     /**
35941      * @cfg {Boolean} encodeDelete False to send only the id to the server on delete, true to encode it in an object
35942      * literal, eg: <pre><code>
35943 {id: 1}
35944  * </code></pre> Defaults to <tt>false</tt>
35945      */
35946     encodeDelete: false,
35947     
35948     constructor : function(config){
35949         Ext.data.JsonWriter.superclass.constructor.call(this, config);    
35950     },
35951
35952     /**
35953      * Final action of a write event.  Apply the written data-object to params.
35954      * @param {Object} http params-object to write-to.
35955      * @param {Object} baseParams as defined by {@link Ext.data.Store#baseParams}.  The baseParms must be encoded by the extending class, eg: {@link Ext.data.JsonWriter}, {@link Ext.data.XmlWriter}.
35956      * @param {Object/Object[]} data Data-object representing compiled Store-recordset.
35957      */
35958     render : function(params, baseParams, data) {
35959         if (this.encode === true) {
35960             // Encode here now.
35961             Ext.apply(params, baseParams);
35962             params[this.meta.root] = Ext.encode(data);
35963         } else {
35964             // defer encoding for some other layer, probably in {@link Ext.Ajax#request}.  Place everything into "jsonData" key.
35965             var jdata = Ext.apply({}, baseParams);
35966             jdata[this.meta.root] = data;
35967             params.jsonData = jdata;
35968         }
35969     },
35970     /**
35971      * Implements abstract Ext.data.DataWriter#createRecord
35972      * @protected
35973      * @param {Ext.data.Record} rec
35974      * @return {Object}
35975      */
35976     createRecord : function(rec) {
35977        return this.toHash(rec);
35978     },
35979     /**
35980      * Implements abstract Ext.data.DataWriter#updateRecord
35981      * @protected
35982      * @param {Ext.data.Record} rec
35983      * @return {Object}
35984      */
35985     updateRecord : function(rec) {
35986         return this.toHash(rec);
35987
35988     },
35989     /**
35990      * Implements abstract Ext.data.DataWriter#destroyRecord
35991      * @protected
35992      * @param {Ext.data.Record} rec
35993      * @return {Object}
35994      */
35995     destroyRecord : function(rec){
35996         if(this.encodeDelete){
35997             var data = {};
35998             data[this.meta.idProperty] = rec.id;
35999             return data;
36000         }else{
36001             return rec.id;
36002         }
36003     }
36004 });/**
36005  * @class Ext.data.JsonReader
36006  * @extends Ext.data.DataReader
36007  * <p>Data reader class to create an Array of {@link Ext.data.Record} objects
36008  * from a JSON packet based on mappings in a provided {@link Ext.data.Record}
36009  * constructor.</p>
36010  * <p>Example code:</p>
36011  * <pre><code>
36012 var myReader = new Ext.data.JsonReader({
36013     // metadata configuration options:
36014     {@link #idProperty}: 'id'
36015     {@link #root}: 'rows',
36016     {@link #totalProperty}: 'results',
36017     {@link Ext.data.DataReader#messageProperty}: "msg"  // The element within the response that provides a user-feedback message (optional)
36018
36019     // the fields config option will internally create an {@link Ext.data.Record}
36020     // constructor that provides mapping for reading the record data objects
36021     {@link Ext.data.DataReader#fields fields}: [
36022         // map Record&#39;s 'firstname' field to data object&#39;s key of same name
36023         {name: 'name'},
36024         // map Record&#39;s 'job' field to data object&#39;s 'occupation' key
36025         {name: 'job', mapping: 'occupation'}
36026     ]
36027 });
36028 </code></pre>
36029  * <p>This would consume a JSON data object of the form:</p><pre><code>
36030 {
36031     results: 2000, // Reader&#39;s configured {@link #totalProperty}
36032     rows: [        // Reader&#39;s configured {@link #root}
36033         // record data objects:
36034         { {@link #idProperty id}: 1, firstname: 'Bill', occupation: 'Gardener' },
36035         { {@link #idProperty id}: 2, firstname: 'Ben' , occupation: 'Horticulturalist' },
36036         ...
36037     ]
36038 }
36039 </code></pre>
36040  * <p><b><u>Automatic configuration using metaData</u></b></p>
36041  * <p>It is possible to change a JsonReader's metadata at any time by including
36042  * a <b><tt>metaData</tt></b> property in the JSON data object. If the JSON data
36043  * object has a <b><tt>metaData</tt></b> property, a {@link Ext.data.Store Store}
36044  * object using this Reader will reconfigure itself to use the newly provided
36045  * field definition and fire its {@link Ext.data.Store#metachange metachange}
36046  * event. The metachange event handler may interrogate the <b><tt>metaData</tt></b>
36047  * property to perform any configuration required.</p>
36048  * <p>Note that reconfiguring a Store potentially invalidates objects which may
36049  * refer to Fields or Records which no longer exist.</p>
36050  * <p>To use this facility you would create the JsonReader like this:</p><pre><code>
36051 var myReader = new Ext.data.JsonReader();
36052 </code></pre>
36053  * <p>The first data packet from the server would configure the reader by
36054  * containing a <b><tt>metaData</tt></b> property <b>and</b> the data. For
36055  * example, the JSON data object might take the form:</p><pre><code>
36056 {
36057     metaData: {
36058         "{@link #idProperty}": "id",
36059         "{@link #root}": "rows",
36060         "{@link #totalProperty}": "results"
36061         "{@link #successProperty}": "success",
36062         "{@link Ext.data.DataReader#fields fields}": [
36063             {"name": "name"},
36064             {"name": "job", "mapping": "occupation"}
36065         ],
36066         // used by store to set its sortInfo
36067         "sortInfo":{
36068            "field": "name",
36069            "direction": "ASC"
36070         },
36071         // {@link Ext.PagingToolbar paging data} (if applicable)
36072         "start": 0,
36073         "limit": 2,
36074         // custom property
36075         "foo": "bar"
36076     },
36077     // Reader&#39;s configured {@link #successProperty}
36078     "success": true,
36079     // Reader&#39;s configured {@link #totalProperty}
36080     "results": 2000,
36081     // Reader&#39;s configured {@link #root}
36082     // (this data simulates 2 results {@link Ext.PagingToolbar per page})
36083     "rows": [ // <b>*Note:</b> this must be an Array
36084         { "id": 1, "name": "Bill", "occupation": "Gardener" },
36085         { "id": 2, "name":  "Ben", "occupation": "Horticulturalist" }
36086     ]
36087 }
36088  * </code></pre>
36089  * <p>The <b><tt>metaData</tt></b> property in the JSON data object should contain:</p>
36090  * <div class="mdetail-params"><ul>
36091  * <li>any of the configuration options for this class</li>
36092  * <li>a <b><tt>{@link Ext.data.Record#fields fields}</tt></b> property which
36093  * the JsonReader will use as an argument to the
36094  * {@link Ext.data.Record#create data Record create method} in order to
36095  * configure the layout of the Records it will produce.</li>
36096  * <li>a <b><tt>{@link Ext.data.Store#sortInfo sortInfo}</tt></b> property
36097  * which the JsonReader will use to set the {@link Ext.data.Store}'s
36098  * {@link Ext.data.Store#sortInfo sortInfo} property</li>
36099  * <li>any custom properties needed</li>
36100  * </ul></div>
36101  *
36102  * @constructor
36103  * Create a new JsonReader
36104  * @param {Object} meta Metadata configuration options.
36105  * @param {Array/Object} recordType
36106  * <p>Either an Array of {@link Ext.data.Field Field} definition objects (which
36107  * will be passed to {@link Ext.data.Record#create}, or a {@link Ext.data.Record Record}
36108  * constructor created from {@link Ext.data.Record#create}.</p>
36109  */
36110 Ext.data.JsonReader = function(meta, recordType){
36111     meta = meta || {};
36112     /**
36113      * @cfg {String} idProperty [id] Name of the property within a row object
36114      * that contains a record identifier value.  Defaults to <tt>id</tt>
36115      */
36116     /**
36117      * @cfg {String} successProperty [success] Name of the property from which to
36118      * retrieve the success attribute. Defaults to <tt>success</tt>.  See
36119      * {@link Ext.data.DataProxy}.{@link Ext.data.DataProxy#exception exception}
36120      * for additional information.
36121      */
36122     /**
36123      * @cfg {String} totalProperty [total] Name of the property from which to
36124      * retrieve the total number of records in the dataset. This is only needed
36125      * if the whole dataset is not passed in one go, but is being paged from
36126      * the remote server.  Defaults to <tt>total</tt>.
36127      */
36128     /**
36129      * @cfg {String} root [undefined] <b>Required</b>.  The name of the property
36130      * which contains the Array of row objects.  Defaults to <tt>undefined</tt>.
36131      * An exception will be thrown if the root property is undefined. The data
36132      * packet value for this property should be an empty array to clear the data
36133      * or show no data.
36134      */
36135     Ext.applyIf(meta, {
36136         idProperty: 'id',
36137         successProperty: 'success',
36138         totalProperty: 'total'
36139     });
36140
36141     Ext.data.JsonReader.superclass.constructor.call(this, meta, recordType || meta.fields);
36142 };
36143 Ext.extend(Ext.data.JsonReader, Ext.data.DataReader, {
36144     /**
36145      * This JsonReader's metadata as passed to the constructor, or as passed in
36146      * the last data packet's <b><tt>metaData</tt></b> property.
36147      * @type Mixed
36148      * @property meta
36149      */
36150     /**
36151      * This method is only used by a DataProxy which has retrieved data from a remote server.
36152      * @param {Object} response The XHR object which contains the JSON data in its responseText.
36153      * @return {Object} data A data block which is used by an Ext.data.Store object as
36154      * a cache of Ext.data.Records.
36155      */
36156     read : function(response){
36157         var json = response.responseText;
36158         var o = Ext.decode(json);
36159         if(!o) {
36160             throw {message: 'JsonReader.read: Json object not found'};
36161         }
36162         return this.readRecords(o);
36163     },
36164
36165     /**
36166      * Decode a json response from server.
36167      * @param {String} action [Ext.data.Api.actions.create|read|update|destroy]
36168      * @param {Object} response
36169      * TODO: refactor code between JsonReader#readRecords, #readResponse into 1 method.
36170      * there's ugly duplication going on due to maintaining backwards compat. with 2.0.  It's time to do this.
36171      */
36172     readResponse : function(action, response) {
36173         var o = (response.responseText !== undefined) ? Ext.decode(response.responseText) : response;
36174         if(!o) {
36175             throw new Ext.data.JsonReader.Error('response');
36176         }
36177
36178         var root = this.getRoot(o);
36179         if (action === Ext.data.Api.actions.create) {
36180             var def = Ext.isDefined(root);
36181             if (def && Ext.isEmpty(root)) {
36182                 throw new Ext.data.JsonReader.Error('root-empty', this.meta.root);
36183             }
36184             else if (!def) {
36185                 throw new Ext.data.JsonReader.Error('root-undefined-response', this.meta.root);
36186             }
36187         }
36188
36189         // instantiate response object
36190         var res = new Ext.data.Response({
36191             action: action,
36192             success: this.getSuccess(o),
36193             data: (root) ? this.extractData(root, false) : [],
36194             message: this.getMessage(o),
36195             raw: o
36196         });
36197
36198         // blow up if no successProperty
36199         if (Ext.isEmpty(res.success)) {
36200             throw new Ext.data.JsonReader.Error('successProperty-response', this.meta.successProperty);
36201         }
36202         return res;
36203     },
36204
36205     /**
36206      * Create a data block containing Ext.data.Records from a JSON object.
36207      * @param {Object} o An object which contains an Array of row objects in the property specified
36208      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
36209      * which contains the total size of the dataset.
36210      * @return {Object} data A data block which is used by an Ext.data.Store object as
36211      * a cache of Ext.data.Records.
36212      */
36213     readRecords : function(o){
36214         /**
36215          * After any data loads, the raw JSON data is available for further custom processing.  If no data is
36216          * loaded or there is a load exception this property will be undefined.
36217          * @type Object
36218          */
36219         this.jsonData = o;
36220         if(o.metaData){
36221             this.onMetaChange(o.metaData);
36222         }
36223         var s = this.meta, Record = this.recordType,
36224             f = Record.prototype.fields, fi = f.items, fl = f.length, v;
36225
36226         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
36227         if(s.totalProperty){
36228             v = parseInt(this.getTotal(o), 10);
36229             if(!isNaN(v)){
36230                 totalRecords = v;
36231             }
36232         }
36233         if(s.successProperty){
36234             v = this.getSuccess(o);
36235             if(v === false || v === 'false'){
36236                 success = false;
36237             }
36238         }
36239
36240         // TODO return Ext.data.Response instance instead.  @see #readResponse
36241         return {
36242             success : success,
36243             records : this.extractData(root, true), // <-- true to return [Ext.data.Record]
36244             totalRecords : totalRecords
36245         };
36246     },
36247
36248     // private
36249     buildExtractors : function() {
36250         if(this.ef){
36251             return;
36252         }
36253         var s = this.meta, Record = this.recordType,
36254             f = Record.prototype.fields, fi = f.items, fl = f.length;
36255
36256         if(s.totalProperty) {
36257             this.getTotal = this.createAccessor(s.totalProperty);
36258         }
36259         if(s.successProperty) {
36260             this.getSuccess = this.createAccessor(s.successProperty);
36261         }
36262         if (s.messageProperty) {
36263             this.getMessage = this.createAccessor(s.messageProperty);
36264         }
36265         this.getRoot = s.root ? this.createAccessor(s.root) : function(p){return p;};
36266         if (s.id || s.idProperty) {
36267             var g = this.createAccessor(s.id || s.idProperty);
36268             this.getId = function(rec) {
36269                 var r = g(rec);
36270                 return (r === undefined || r === '') ? null : r;
36271             };
36272         } else {
36273             this.getId = function(){return null;};
36274         }
36275         var ef = [];
36276         for(var i = 0; i < fl; i++){
36277             f = fi[i];
36278             var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
36279             ef.push(this.createAccessor(map));
36280         }
36281         this.ef = ef;
36282     },
36283
36284     /**
36285      * @ignore
36286      * TODO This isn't used anywhere??  Don't we want to use this where possible instead of complex #createAccessor?
36287      */
36288     simpleAccess : function(obj, subsc) {
36289         return obj[subsc];
36290     },
36291
36292     /**
36293      * @ignore
36294      */
36295     createAccessor : function(){
36296         var re = /[\[\.]/;
36297         return function(expr) {
36298             if(Ext.isEmpty(expr)){
36299                 return Ext.emptyFn;
36300             }
36301             if(Ext.isFunction(expr)){
36302                 return expr;
36303             }
36304             var i = String(expr).search(re);
36305             if(i >= 0){
36306                 return new Function('obj', 'return obj' + (i > 0 ? '.' : '') + expr);
36307             }
36308             return function(obj){
36309                 return obj[expr];
36310             };
36311
36312         };
36313     }(),
36314
36315     /**
36316      * type-casts a single row of raw-data from server
36317      * @param {Object} data
36318      * @param {Array} items
36319      * @param {Integer} len
36320      * @private
36321      */
36322     extractValues : function(data, items, len) {
36323         var f, values = {};
36324         for(var j = 0; j < len; j++){
36325             f = items[j];
36326             var v = this.ef[j](data);
36327             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue, data);
36328         }
36329         return values;
36330     }
36331 });
36332
36333 /**
36334  * @class Ext.data.JsonReader.Error
36335  * Error class for JsonReader
36336  */
36337 Ext.data.JsonReader.Error = Ext.extend(Ext.Error, {
36338     constructor : function(message, arg) {
36339         this.arg = arg;
36340         Ext.Error.call(this, message);
36341     },
36342     name : 'Ext.data.JsonReader'
36343 });
36344 Ext.apply(Ext.data.JsonReader.Error.prototype, {
36345     lang: {
36346         'response': 'An error occurred while json-decoding your server response',
36347         'successProperty-response': 'Could not locate your "successProperty" in your server response.  Please review your JsonReader config to ensure the config-property "successProperty" matches the property in your server-response.  See the JsonReader docs.',
36348         'root-undefined-config': 'Your JsonReader was configured without a "root" property.  Please review your JsonReader config and make sure to define the root property.  See the JsonReader docs.',
36349         'idProperty-undefined' : 'Your JsonReader was configured without an "idProperty"  Please review your JsonReader configuration and ensure the "idProperty" is set (e.g.: "id").  See the JsonReader docs.',
36350         'root-empty': 'Data was expected to be returned by the server in the "root" property of the response.  Please review your JsonReader configuration to ensure the "root" property matches that returned in the server-response.  See JsonReader docs.'
36351     }
36352 });
36353 /**
36354  * @class Ext.data.ArrayReader
36355  * @extends Ext.data.JsonReader
36356  * <p>Data reader class to create an Array of {@link Ext.data.Record} objects from an Array.
36357  * Each element of that Array represents a row of data fields. The
36358  * fields are pulled into a Record object using as a subscript, the <code>mapping</code> property
36359  * of the field definition if it exists, or the field's ordinal position in the definition.</p>
36360  * <p>Example code:</p>
36361  * <pre><code>
36362 var Employee = Ext.data.Record.create([
36363     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
36364     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
36365 ]);
36366 var myReader = new Ext.data.ArrayReader({
36367     {@link #idIndex}: 0
36368 }, Employee);
36369 </code></pre>
36370  * <p>This would consume an Array like this:</p>
36371  * <pre><code>
36372 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
36373  * </code></pre>
36374  * @constructor
36375  * Create a new ArrayReader
36376  * @param {Object} meta Metadata configuration options.
36377  * @param {Array/Object} recordType
36378  * <p>Either an Array of {@link Ext.data.Field Field} definition objects (which
36379  * will be passed to {@link Ext.data.Record#create}, or a {@link Ext.data.Record Record}
36380  * constructor created from {@link Ext.data.Record#create}.</p>
36381  */
36382 Ext.data.ArrayReader = Ext.extend(Ext.data.JsonReader, {
36383     /**
36384      * @cfg {String} successProperty
36385      * @hide
36386      */
36387     /**
36388      * @cfg {Number} id (optional) The subscript within row Array that provides an ID for the Record.
36389      * Deprecated. Use {@link #idIndex} instead.
36390      */
36391     /**
36392      * @cfg {Number} idIndex (optional) The subscript within row Array that provides an ID for the Record.
36393      */
36394     /**
36395      * Create a data block containing Ext.data.Records from an Array.
36396      * @param {Object} o An Array of row objects which represents the dataset.
36397      * @return {Object} data A data block which is used by an Ext.data.Store object as
36398      * a cache of Ext.data.Records.
36399      */
36400     readRecords : function(o){
36401         this.arrayData = o;
36402         var s = this.meta,
36403             sid = s ? Ext.num(s.idIndex, s.id) : null,
36404             recordType = this.recordType,
36405             fields = recordType.prototype.fields,
36406             records = [],
36407             success = true,
36408             v;
36409
36410         var root = this.getRoot(o);
36411
36412         for(var i = 0, len = root.length; i < len; i++) {
36413             var n = root[i],
36414                 values = {},
36415                 id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
36416             for(var j = 0, jlen = fields.length; j < jlen; j++) {
36417                 var f = fields.items[j],
36418                     k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
36419                 v = n[k] !== undefined ? n[k] : f.defaultValue;
36420                 v = f.convert(v, n);
36421                 values[f.name] = v;
36422             }
36423             var record = new recordType(values, id);
36424             record.json = n;
36425             records[records.length] = record;
36426         }
36427
36428         var totalRecords = records.length;
36429
36430         if(s.totalProperty) {
36431             v = parseInt(this.getTotal(o), 10);
36432             if(!isNaN(v)) {
36433                 totalRecords = v;
36434             }
36435         }
36436         if(s.successProperty){
36437             v = this.getSuccess(o);
36438             if(v === false || v === 'false'){
36439                 success = false;
36440             }
36441         }
36442
36443         return {
36444             success : success,
36445             records : records,
36446             totalRecords : totalRecords
36447         };
36448     }
36449 });/**
36450  * @class Ext.data.ArrayStore
36451  * @extends Ext.data.Store
36452  * <p>Formerly known as "SimpleStore".</p>
36453  * <p>Small helper class to make creating {@link Ext.data.Store}s from Array data easier.
36454  * An ArrayStore will be automatically configured with a {@link Ext.data.ArrayReader}.</p>
36455  * <p>A store configuration would be something like:<pre><code>
36456 var store = new Ext.data.ArrayStore({
36457     // store configs
36458     autoDestroy: true,
36459     storeId: 'myStore',
36460     // reader configs
36461     idIndex: 0,  
36462     fields: [
36463        'company',
36464        {name: 'price', type: 'float'},
36465        {name: 'change', type: 'float'},
36466        {name: 'pctChange', type: 'float'},
36467        {name: 'lastChange', type: 'date', dateFormat: 'n/j h:ia'}
36468     ]
36469 });
36470  * </code></pre></p>
36471  * <p>This store is configured to consume a returned object of the form:<pre><code>
36472 var myData = [
36473     ['3m Co',71.72,0.02,0.03,'9/1 12:00am'],
36474     ['Alcoa Inc',29.01,0.42,1.47,'9/1 12:00am'],
36475     ['Boeing Co.',75.43,0.53,0.71,'9/1 12:00am'],
36476     ['Hewlett-Packard Co.',36.53,-0.03,-0.08,'9/1 12:00am'],
36477     ['Wal-Mart Stores, Inc.',45.45,0.73,1.63,'9/1 12:00am']
36478 ];
36479  * </code></pre>
36480  * An object literal of this form could also be used as the {@link #data} config option.</p>
36481  * <p><b>*Note:</b> Although not listed here, this class accepts all of the configuration options of 
36482  * <b>{@link Ext.data.ArrayReader ArrayReader}</b>.</p>
36483  * @constructor
36484  * @param {Object} config
36485  * @xtype arraystore
36486  */
36487 Ext.data.ArrayStore = Ext.extend(Ext.data.Store, {
36488     /**
36489      * @cfg {Ext.data.DataReader} reader @hide
36490      */
36491     constructor: function(config){
36492         Ext.data.ArrayStore.superclass.constructor.call(this, Ext.apply(config, {
36493             reader: new Ext.data.ArrayReader(config)
36494         }));
36495     },
36496
36497     loadData : function(data, append){
36498         if(this.expandData === true){
36499             var r = [];
36500             for(var i = 0, len = data.length; i < len; i++){
36501                 r[r.length] = [data[i]];
36502             }
36503             data = r;
36504         }
36505         Ext.data.ArrayStore.superclass.loadData.call(this, data, append);
36506     }
36507 });
36508 Ext.reg('arraystore', Ext.data.ArrayStore);
36509
36510 // backwards compat
36511 Ext.data.SimpleStore = Ext.data.ArrayStore;
36512 Ext.reg('simplestore', Ext.data.SimpleStore);/**
36513  * @class Ext.data.JsonStore
36514  * @extends Ext.data.Store
36515  * <p>Small helper class to make creating {@link Ext.data.Store}s from JSON data easier.
36516  * A JsonStore will be automatically configured with a {@link Ext.data.JsonReader}.</p>
36517  * <p>A store configuration would be something like:<pre><code>
36518 var store = new Ext.data.JsonStore({
36519     // store configs
36520     autoDestroy: true,
36521     url: 'get-images.php',
36522     storeId: 'myStore',
36523     // reader configs
36524     root: 'images',
36525     idProperty: 'name',
36526     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
36527 });
36528  * </code></pre></p>
36529  * <p>This store is configured to consume a returned object of the form:<pre><code>
36530 {
36531     images: [
36532         {name: 'Image one', url:'/GetImage.php?id=1', size:46.5, lastmod: new Date(2007, 10, 29)},
36533         {name: 'Image Two', url:'/GetImage.php?id=2', size:43.2, lastmod: new Date(2007, 10, 30)}
36534     ]
36535 }
36536  * </code></pre>
36537  * An object literal of this form could also be used as the {@link #data} config option.</p>
36538  * <p><b>*Note:</b> Although not listed here, this class accepts all of the configuration options of
36539  * <b>{@link Ext.data.JsonReader JsonReader}</b>.</p>
36540  * @constructor
36541  * @param {Object} config
36542  * @xtype jsonstore
36543  */
36544 Ext.data.JsonStore = Ext.extend(Ext.data.Store, {
36545     /**
36546      * @cfg {Ext.data.DataReader} reader @hide
36547      */
36548     constructor: function(config){
36549         Ext.data.JsonStore.superclass.constructor.call(this, Ext.apply(config, {
36550             reader: new Ext.data.JsonReader(config)
36551         }));
36552     }
36553 });
36554 Ext.reg('jsonstore', Ext.data.JsonStore);/**
36555  * @class Ext.data.XmlWriter
36556  * @extends Ext.data.DataWriter
36557  * DataWriter extension for writing an array or single {@link Ext.data.Record} object(s) in preparation for executing a remote CRUD action via XML.
36558  * XmlWriter uses an instance of {@link Ext.XTemplate} for maximum flexibility in defining your own custom XML schema if the default schema is not appropriate for your needs.
36559  * See the {@link #tpl} configuration-property.
36560  */
36561 Ext.data.XmlWriter = function(params) {
36562     Ext.data.XmlWriter.superclass.constructor.apply(this, arguments);
36563     // compile the XTemplate for rendering XML documents.
36564     this.tpl = (typeof(this.tpl) === 'string') ? new Ext.XTemplate(this.tpl).compile() : this.tpl.compile();
36565 };
36566 Ext.extend(Ext.data.XmlWriter, Ext.data.DataWriter, {
36567     /**
36568      * @cfg {String} documentRoot [xrequest] (Optional) The name of the XML document root-node.  <b>Note:</b>
36569      * this parameter is required </b>only when</b> sending extra {@link Ext.data.Store#baseParams baseParams} to the server
36570      * during a write-request -- if no baseParams are set, the {@link Ext.data.XmlReader#record} meta-property can
36571      * suffice as the XML document root-node for write-actions involving just a <b>single record</b>.  For requests
36572      * involving <b>multiple</b> records and <b>NO</b> baseParams, the {@link Ext.data.XmlWriter#root} property can
36573      * act as the XML document root.
36574      */
36575     documentRoot: 'xrequest',
36576     /**
36577      * @cfg {Boolean} forceDocumentRoot [false] Set to <tt>true</tt> to force XML documents having a root-node as defined
36578      * by {@link #documentRoot}, even with no baseParams defined.
36579      */
36580     forceDocumentRoot: false,
36581     /**
36582      * @cfg {String} root [records] The name of the containing element which will contain the nodes of an write-action involving <b>multiple</b> records.  Each
36583      * xml-record written to the server will be wrapped in an element named after {@link Ext.data.XmlReader#record} property.
36584      * eg:
36585 <code><pre>
36586 &lt;?xml version="1.0" encoding="UTF-8"?>
36587 &lt;user>&lt;first>Barney&lt;/first>&lt;/user>
36588 </code></pre>
36589      * However, when <b>multiple</b> records are written in a batch-operation, these records must be wrapped in a containing
36590      * Element.
36591      * eg:
36592 <code><pre>
36593 &lt;?xml version="1.0" encoding="UTF-8"?>
36594     &lt;records>
36595         &lt;first>Barney&lt;/first>&lt;/user>
36596         &lt;records>&lt;first>Barney&lt;/first>&lt;/user>
36597     &lt;/records>
36598 </code></pre>
36599      * Defaults to <tt>records</tt>.  Do not confuse the nature of this property with that of {@link #documentRoot}
36600      */
36601     root: 'records',
36602     /**
36603      * @cfg {String} xmlVersion [1.0] The <tt>version</tt> written to header of xml documents.
36604 <code><pre>&lt;?xml version="1.0" encoding="ISO-8859-15"?></pre></code>
36605      */
36606     xmlVersion : '1.0',
36607     /**
36608      * @cfg {String} xmlEncoding [ISO-8859-15] The <tt>encoding</tt> written to header of xml documents.
36609 <code><pre>&lt;?xml version="1.0" encoding="ISO-8859-15"?></pre></code>
36610      */
36611     xmlEncoding: 'ISO-8859-15',
36612     /**
36613      * @cfg {String/Ext.XTemplate} tpl The XML template used to render {@link Ext.data.Api#actions write-actions} to your server.
36614      * <p>One can easily provide his/her own custom {@link Ext.XTemplate#constructor template-definition} if the default does not suffice.</p>
36615      * <p>Defaults to:</p>
36616 <code><pre>
36617 &lt;?xml version="{version}" encoding="{encoding}"?>
36618     &lt;tpl if="documentRoot">&lt;{documentRoot}>
36619     &lt;tpl for="baseParams">
36620         &lt;tpl for=".">
36621             &lt;{name}>{value}&lt;/{name}>
36622         &lt;/tpl>
36623     &lt;/tpl>
36624     &lt;tpl if="records.length &gt; 1">&lt;{root}>',
36625     &lt;tpl for="records">
36626         &lt;{parent.record}>
36627         &lt;tpl for=".">
36628             &lt;{name}>{value}&lt;/{name}>
36629         &lt;/tpl>
36630         &lt;/{parent.record}>
36631     &lt;/tpl>
36632     &lt;tpl if="records.length &gt; 1">&lt;/{root}>&lt;/tpl>
36633     &lt;tpl if="documentRoot">&lt;/{documentRoot}>&lt;/tpl>
36634 </pre></code>
36635      * <p>Templates will be called with the following API</p>
36636      * <ul>
36637      * <li>{String} version [1.0] The xml version.</li>
36638      * <li>{String} encoding [ISO-8859-15] The xml encoding.</li>
36639      * <li>{String/false} documentRoot The XML document root-node name or <tt>false</tt> if not required.  See {@link #documentRoot} and {@link #forceDocumentRoot}.</li>
36640      * <li>{String} record The meta-data parameter defined on your {@link Ext.data.XmlReader#record} configuration represents the name of the xml-tag containing each record.</li>
36641      * <li>{String} root The meta-data parameter defined by {@link Ext.data.XmlWriter#root} configuration-parameter.  Represents the name of the xml root-tag when sending <b>multiple</b> records to the server.</li>
36642      * <li>{Array} records The records being sent to the server, ie: the subject of the write-action being performed.  The records parameter will be always be an array, even when only a single record is being acted upon.
36643      *     Each item within the records array will contain an array of field objects having the following properties:
36644      *     <ul>
36645      *         <li>{String} name The field-name of the record as defined by your {@link Ext.data.Record#create Ext.data.Record definition}.  The "mapping" property will be used, otherwise it will match the "name" property.  Use this parameter to define the XML tag-name of the property.</li>
36646      *         <li>{Mixed} value The record value of the field enclosed within XML tags specified by name property above.</li>
36647      *     </ul></li>
36648      * <li>{Array} baseParams.  The baseParams as defined upon {@link Ext.data.Store#baseParams}.  Note that the baseParams have been converted into an array of [{name : "foo", value: "bar"}, ...] pairs in the same manner as the <b>records</b> parameter above.  See {@link #documentRoot} and {@link #forceDocumentRoot}.</li>
36649      * </ul>
36650      */
36651     // Break up encoding here in case it's being included by some kind of page that will parse it (eg. PHP)
36652     tpl: '<tpl for="."><' + '?xml version="{version}" encoding="{encoding}"?' + '><tpl if="documentRoot"><{documentRoot}><tpl for="baseParams"><tpl for="."><{name}>{value}</{name}</tpl></tpl></tpl><tpl if="records.length&gt;1"><{root}></tpl><tpl for="records"><{parent.record}><tpl for="."><{name}>{value}</{name}></tpl></{parent.record}></tpl><tpl if="records.length&gt;1"></{root}></tpl><tpl if="documentRoot"></{documentRoot}></tpl></tpl>',
36653
36654     /**
36655      * XmlWriter implementation of the final stage of a write action.
36656      * @param {Object} params Transport-proxy's (eg: {@link Ext.Ajax#request}) params-object to write-to.
36657      * @param {Object} baseParams as defined by {@link Ext.data.Store#baseParams}.  The baseParms must be encoded by the extending class, eg: {@link Ext.data.JsonWriter}, {@link Ext.data.XmlWriter}.
36658      * @param {Object/Object[]} data Data-object representing the compiled Store-recordset.
36659      */
36660     render : function(params, baseParams, data) {
36661         baseParams = this.toArray(baseParams);
36662         params.xmlData = this.tpl.applyTemplate({
36663             version: this.xmlVersion,
36664             encoding: this.xmlEncoding,
36665             documentRoot: (baseParams.length > 0 || this.forceDocumentRoot === true) ? this.documentRoot : false,
36666             record: this.meta.record,
36667             root: this.root,
36668             baseParams: baseParams,
36669             records: (Ext.isArray(data[0])) ? data : [data]
36670         });
36671     },
36672
36673     /**
36674      * createRecord
36675      * @protected
36676      * @param {Ext.data.Record} rec
36677      * @return {Array} Array of <tt>name:value</tt> pairs for attributes of the {@link Ext.data.Record}.  See {@link Ext.data.DataWriter#toHash}.
36678      */
36679     createRecord : function(rec) {
36680         return this.toArray(this.toHash(rec));
36681     },
36682
36683     /**
36684      * updateRecord
36685      * @protected
36686      * @param {Ext.data.Record} rec
36687      * @return {Array} Array of {name:value} pairs for attributes of the {@link Ext.data.Record}.  See {@link Ext.data.DataWriter#toHash}.
36688      */
36689     updateRecord : function(rec) {
36690         return this.toArray(this.toHash(rec));
36691
36692     },
36693     /**
36694      * destroyRecord
36695      * @protected
36696      * @param {Ext.data.Record} rec
36697      * @return {Array} Array containing a attribute-object (name/value pair) representing the {@link Ext.data.DataReader#idProperty idProperty}.
36698      */
36699     destroyRecord : function(rec) {
36700         var data = {};
36701         data[this.meta.idProperty] = rec.id;
36702         return this.toArray(data);
36703     }
36704 });
36705
36706 /**
36707  * @class Ext.data.XmlReader
36708  * @extends Ext.data.DataReader
36709  * <p>Data reader class to create an Array of {@link Ext.data.Record} objects from an XML document
36710  * based on mappings in a provided {@link Ext.data.Record} constructor.</p>
36711  * <p><b>Note</b>: that in order for the browser to parse a returned XML document, the Content-Type
36712  * header in the HTTP response must be set to "text/xml" or "application/xml".</p>
36713  * <p>Example code:</p>
36714  * <pre><code>
36715 var Employee = Ext.data.Record.create([
36716    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it is the same as "name"
36717    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
36718 ]);
36719 var myReader = new Ext.data.XmlReader({
36720    totalProperty: "results", // The element which contains the total dataset size (optional)
36721    record: "row",           // The repeated element which contains row information
36722    idProperty: "id"         // The element within the row that provides an ID for the record (optional)
36723    messageProperty: "msg"   // The element within the response that provides a user-feedback message (optional)
36724 }, Employee);
36725 </code></pre>
36726  * <p>
36727  * This would consume an XML file like this:
36728  * <pre><code>
36729 &lt;?xml version="1.0" encoding="UTF-8"?>
36730 &lt;dataset>
36731  &lt;results>2&lt;/results>
36732  &lt;row>
36733    &lt;id>1&lt;/id>
36734    &lt;name>Bill&lt;/name>
36735    &lt;occupation>Gardener&lt;/occupation>
36736  &lt;/row>
36737  &lt;row>
36738    &lt;id>2&lt;/id>
36739    &lt;name>Ben&lt;/name>
36740    &lt;occupation>Horticulturalist&lt;/occupation>
36741  &lt;/row>
36742 &lt;/dataset>
36743 </code></pre>
36744  * @cfg {String} totalProperty The DomQuery path from which to retrieve the total number of records
36745  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
36746  * paged from the remote server.
36747  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
36748  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
36749  * @cfg {String} successProperty The DomQuery path to the success attribute used by forms.
36750  * @cfg {String} idPath The DomQuery path relative from the record element to the element that contains
36751  * a record identifier value.
36752  * @constructor
36753  * Create a new XmlReader.
36754  * @param {Object} meta Metadata configuration options
36755  * @param {Object} recordType Either an Array of field definition objects as passed to
36756  * {@link Ext.data.Record#create}, or a Record constructor object created using {@link Ext.data.Record#create}.
36757  */
36758 Ext.data.XmlReader = function(meta, recordType){
36759     meta = meta || {};
36760
36761     // backwards compat, convert idPath or id / success
36762     Ext.applyIf(meta, {
36763         idProperty: meta.idProperty || meta.idPath || meta.id,
36764         successProperty: meta.successProperty || meta.success
36765     });
36766
36767     Ext.data.XmlReader.superclass.constructor.call(this, meta, recordType || meta.fields);
36768 };
36769 Ext.extend(Ext.data.XmlReader, Ext.data.DataReader, {
36770     /**
36771      * This method is only used by a DataProxy which has retrieved data from a remote server.
36772      * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
36773      * to contain a property called <tt>responseXML</tt> which refers to an XML document object.
36774      * @return {Object} records A data block which is used by an {@link Ext.data.Store} as
36775      * a cache of Ext.data.Records.
36776      */
36777     read : function(response){
36778         var doc = response.responseXML;
36779         if(!doc) {
36780             throw {message: "XmlReader.read: XML Document not available"};
36781         }
36782         return this.readRecords(doc);
36783     },
36784
36785     /**
36786      * Create a data block containing Ext.data.Records from an XML document.
36787      * @param {Object} doc A parsed XML document.
36788      * @return {Object} records A data block which is used by an {@link Ext.data.Store} as
36789      * a cache of Ext.data.Records.
36790      */
36791     readRecords : function(doc){
36792         /**
36793          * After any data loads/reads, the raw XML Document is available for further custom processing.
36794          * @type XMLDocument
36795          */
36796         this.xmlData = doc;
36797
36798         var root    = doc.documentElement || doc,
36799             q       = Ext.DomQuery,
36800             totalRecords = 0,
36801             success = true;
36802
36803         if(this.meta.totalProperty){
36804             totalRecords = this.getTotal(root, 0);
36805         }
36806         if(this.meta.successProperty){
36807             success = this.getSuccess(root);
36808         }
36809
36810         var records = this.extractData(q.select(this.meta.record, root), true); // <-- true to return Ext.data.Record[]
36811
36812         // TODO return Ext.data.Response instance.  @see #readResponse
36813         return {
36814             success : success,
36815             records : records,
36816             totalRecords : totalRecords || records.length
36817         };
36818     },
36819
36820     /**
36821      * Decode a json response from server.
36822      * @param {String} action [{@link Ext.data.Api#actions} create|read|update|destroy]
36823      * @param {Object} response HTTP Response object from browser.
36824      * @return {Ext.data.Response} response Returns an instance of {@link Ext.data.Response}
36825      */
36826     readResponse : function(action, response) {
36827         var q   = Ext.DomQuery,
36828         doc     = response.responseXML;
36829
36830         // create general Response instance.
36831         var res = new Ext.data.Response({
36832             action: action,
36833             success : this.getSuccess(doc),
36834             message: this.getMessage(doc),
36835             data: this.extractData(q.select(this.meta.record, doc) || q.select(this.meta.root, doc), false),
36836             raw: doc
36837         });
36838
36839         if (Ext.isEmpty(res.success)) {
36840             throw new Ext.data.DataReader.Error('successProperty-response', this.meta.successProperty);
36841         }
36842
36843         // Create actions from a response having status 200 must return pk
36844         if (action === Ext.data.Api.actions.create) {
36845             var def = Ext.isDefined(res.data);
36846             if (def && Ext.isEmpty(res.data)) {
36847                 throw new Ext.data.JsonReader.Error('root-empty', this.meta.root);
36848             }
36849             else if (!def) {
36850                 throw new Ext.data.JsonReader.Error('root-undefined-response', this.meta.root);
36851             }
36852         }
36853         return res;
36854     },
36855
36856     getSuccess : function() {
36857         return true;
36858     },
36859
36860     /**
36861      * build response-data extractor functions.
36862      * @private
36863      * @ignore
36864      */
36865     buildExtractors : function() {
36866         if(this.ef){
36867             return;
36868         }
36869         var s       = this.meta,
36870             Record  = this.recordType,
36871             f       = Record.prototype.fields,
36872             fi      = f.items,
36873             fl      = f.length;
36874
36875         if(s.totalProperty) {
36876             this.getTotal = this.createAccessor(s.totalProperty);
36877         }
36878         if(s.successProperty) {
36879             this.getSuccess = this.createAccessor(s.successProperty);
36880         }
36881         if (s.messageProperty) {
36882             this.getMessage = this.createAccessor(s.messageProperty);
36883         }
36884         this.getRoot = function(res) {
36885             return (!Ext.isEmpty(res[this.meta.record])) ? res[this.meta.record] : res[this.meta.root];
36886         }
36887         if (s.idPath || s.idProperty) {
36888             var g = this.createAccessor(s.idPath || s.idProperty);
36889             this.getId = function(rec) {
36890                 var id = g(rec) || rec.id;
36891                 return (id === undefined || id === '') ? null : id;
36892             };
36893         } else {
36894             this.getId = function(){return null;};
36895         }
36896         var ef = [];
36897         for(var i = 0; i < fl; i++){
36898             f = fi[i];
36899             var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
36900             ef.push(this.createAccessor(map));
36901         }
36902         this.ef = ef;
36903     },
36904
36905     /**
36906      * Creates a function to return some particular key of data from a response.
36907      * @param {String} key
36908      * @return {Function}
36909      * @private
36910      * @ignore
36911      */
36912     createAccessor : function(){
36913         var q = Ext.DomQuery;
36914         return function(key) {
36915             switch(key) {
36916                 case this.meta.totalProperty:
36917                     return function(root, def){
36918                         return q.selectNumber(key, root, def);
36919                     }
36920                     break;
36921                 case this.meta.successProperty:
36922                     return function(root, def) {
36923                         var sv = q.selectValue(key, root, true);
36924                         var success = sv !== false && sv !== 'false';
36925                         return success;
36926                     }
36927                     break;
36928                 default:
36929                     return function(root, def) {
36930                         return q.selectValue(key, root, def);
36931                     }
36932                     break;
36933             }
36934         };
36935     }(),
36936
36937     /**
36938      * extracts values and type-casts a row of data from server, extracted by #extractData
36939      * @param {Hash} data
36940      * @param {Ext.data.Field[]} items
36941      * @param {Number} len
36942      * @private
36943      * @ignore
36944      */
36945     extractValues : function(data, items, len) {
36946         var f, values = {};
36947         for(var j = 0; j < len; j++){
36948             f = items[j];
36949             var v = this.ef[j](data);
36950             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue, data);
36951         }
36952         return values;
36953     }
36954 });/**\r
36955  * @class Ext.data.XmlStore\r
36956  * @extends Ext.data.Store\r
36957  * <p>Small helper class to make creating {@link Ext.data.Store}s from XML data easier.\r
36958  * A XmlStore will be automatically configured with a {@link Ext.data.XmlReader}.</p>\r
36959  * <p>A store configuration would be something like:<pre><code>\r
36960 var store = new Ext.data.XmlStore({\r
36961     // store configs\r
36962     autoDestroy: true,\r
36963     storeId: 'myStore',\r
36964     url: 'sheldon.xml', // automatically configures a HttpProxy\r
36965     // reader configs\r
36966     record: 'Item', // records will have an "Item" tag\r
36967     idPath: 'ASIN',\r
36968     totalRecords: '@TotalResults'\r
36969     fields: [\r
36970         // set up the fields mapping into the xml doc\r
36971         // The first needs mapping, the others are very basic\r
36972         {name: 'Author', mapping: 'ItemAttributes > Author'},\r
36973         'Title', 'Manufacturer', 'ProductGroup'\r
36974     ]\r
36975 });\r
36976  * </code></pre></p>\r
36977  * <p>This store is configured to consume a returned object of the form:<pre><code>\r
36978 &#60?xml version="1.0" encoding="UTF-8"?>\r
36979 &#60ItemSearchResponse xmlns="http://webservices.amazon.com/AWSECommerceService/2009-05-15">\r
36980     &#60Items>\r
36981         &#60Request>\r
36982             &#60IsValid>True&#60/IsValid>\r
36983             &#60ItemSearchRequest>\r
36984                 &#60Author>Sidney Sheldon&#60/Author>\r
36985                 &#60SearchIndex>Books&#60/SearchIndex>\r
36986             &#60/ItemSearchRequest>\r
36987         &#60/Request>\r
36988         &#60TotalResults>203&#60/TotalResults>\r
36989         &#60TotalPages>21&#60/TotalPages>\r
36990         &#60Item>\r
36991             &#60ASIN>0446355453&#60/ASIN>\r
36992             &#60DetailPageURL>\r
36993                 http://www.amazon.com/\r
36994             &#60/DetailPageURL>\r
36995             &#60ItemAttributes>\r
36996                 &#60Author>Sidney Sheldon&#60/Author>\r
36997                 &#60Manufacturer>Warner Books&#60/Manufacturer>\r
36998                 &#60ProductGroup>Book&#60/ProductGroup>\r
36999                 &#60Title>Master of the Game&#60/Title>\r
37000             &#60/ItemAttributes>\r
37001         &#60/Item>\r
37002     &#60/Items>\r
37003 &#60/ItemSearchResponse>\r
37004  * </code></pre>\r
37005  * An object literal of this form could also be used as the {@link #data} config option.</p>\r
37006  * <p><b>Note:</b> Although not listed here, this class accepts all of the configuration options of \r
37007  * <b>{@link Ext.data.XmlReader XmlReader}</b>.</p>\r
37008  * @constructor\r
37009  * @param {Object} config\r
37010  * @xtype xmlstore\r
37011  */\r
37012 Ext.data.XmlStore = Ext.extend(Ext.data.Store, {\r
37013     /**\r
37014      * @cfg {Ext.data.DataReader} reader @hide\r
37015      */\r
37016     constructor: function(config){\r
37017         Ext.data.XmlStore.superclass.constructor.call(this, Ext.apply(config, {\r
37018             reader: new Ext.data.XmlReader(config)\r
37019         }));\r
37020     }\r
37021 });\r
37022 Ext.reg('xmlstore', Ext.data.XmlStore);/**\r
37023  * @class Ext.data.GroupingStore\r
37024  * @extends Ext.data.Store\r
37025  * A specialized store implementation that provides for grouping records by one of the available fields. This\r
37026  * is usually used in conjunction with an {@link Ext.grid.GroupingView} to proved the data model for\r
37027  * a grouped GridPanel.\r
37028  * @constructor\r
37029  * Creates a new GroupingStore.\r
37030  * @param {Object} config A config object containing the objects needed for the Store to access data,\r
37031  * and read the data into Records.\r
37032  * @xtype groupingstore\r
37033  */\r
37034 Ext.data.GroupingStore = Ext.extend(Ext.data.Store, {\r
37035 \r
37036     //inherit docs\r
37037     constructor: function(config){\r
37038         Ext.data.GroupingStore.superclass.constructor.call(this, config);\r
37039         this.applyGroupField();\r
37040     },\r
37041 \r
37042     /**\r
37043      * @cfg {String} groupField\r
37044      * The field name by which to sort the store's data (defaults to '').\r
37045      */\r
37046     /**\r
37047      * @cfg {Boolean} remoteGroup\r
37048      * True if the grouping should apply on the server side, false if it is local only (defaults to false).  If the\r
37049      * grouping is local, it can be applied immediately to the data.  If it is remote, then it will simply act as a\r
37050      * helper, automatically sending the grouping field name as the 'groupBy' param with each XHR call.\r
37051      */\r
37052     remoteGroup : false,\r
37053     /**\r
37054      * @cfg {Boolean} groupOnSort\r
37055      * True to sort the data on the grouping field when a grouping operation occurs, false to sort based on the\r
37056      * existing sort info (defaults to false).\r
37057      */\r
37058     groupOnSort:false,\r
37059 \r
37060     groupDir : 'ASC',\r
37061 \r
37062     /**\r
37063      * Clears any existing grouping and refreshes the data using the default sort.\r
37064      */\r
37065     clearGrouping : function(){\r
37066         this.groupField = false;\r
37067 \r
37068         if(this.remoteGroup){\r
37069             if(this.baseParams){\r
37070                 delete this.baseParams.groupBy;\r
37071                 delete this.baseParams.groupDir;\r
37072             }\r
37073             var lo = this.lastOptions;\r
37074             if(lo && lo.params){\r
37075                 delete lo.params.groupBy;\r
37076                 delete lo.params.groupDir;\r
37077             }\r
37078 \r
37079             this.reload();\r
37080         }else{\r
37081             this.applySort();\r
37082             this.fireEvent('datachanged', this);\r
37083         }\r
37084     },\r
37085 \r
37086     /**\r
37087      * Groups the data by the specified field.\r
37088      * @param {String} field The field name by which to sort the store's data\r
37089      * @param {Boolean} forceRegroup (optional) True to force the group to be refreshed even if the field passed\r
37090      * in is the same as the current grouping field, false to skip grouping on the same field (defaults to false)\r
37091      */\r
37092     groupBy : function(field, forceRegroup, direction){\r
37093         direction = direction ? (String(direction).toUpperCase() == 'DESC' ? 'DESC' : 'ASC') : this.groupDir;\r
37094         if(this.groupField == field && this.groupDir == direction && !forceRegroup){\r
37095             return; // already grouped by this field\r
37096         }\r
37097         this.groupField = field;\r
37098         this.groupDir = direction;\r
37099         this.applyGroupField();\r
37100         if(this.groupOnSort){\r
37101             this.sort(field, direction);\r
37102             return;\r
37103         }\r
37104         if(this.remoteGroup){\r
37105             this.reload();\r
37106         }else{\r
37107             var si = this.sortInfo || {};\r
37108             if(forceRegroup || si.field != field || si.direction != direction){\r
37109                 this.applySort();\r
37110             }else{\r
37111                 this.sortData(field, direction);\r
37112             }\r
37113             this.fireEvent('datachanged', this);\r
37114         }\r
37115     },\r
37116 \r
37117     // private\r
37118     applyGroupField: function(){\r
37119         if(this.remoteGroup){\r
37120             if(!this.baseParams){\r
37121                 this.baseParams = {};\r
37122             }\r
37123             Ext.apply(this.baseParams, {\r
37124                 groupBy : this.groupField,\r
37125                 groupDir : this.groupDir\r
37126             });\r
37127 \r
37128             var lo = this.lastOptions;\r
37129             if(lo && lo.params){\r
37130                 Ext.apply(lo.params, {\r
37131                     groupBy : this.groupField,\r
37132                     groupDir : this.groupDir\r
37133                 });\r
37134             }\r
37135         }\r
37136     },\r
37137 \r
37138     // private\r
37139     applySort : function(){\r
37140         Ext.data.GroupingStore.superclass.applySort.call(this);\r
37141         if(!this.groupOnSort && !this.remoteGroup){\r
37142             var gs = this.getGroupState();\r
37143             if(gs && (gs != this.sortInfo.field || this.groupDir != this.sortInfo.direction)){\r
37144                 this.sortData(this.groupField, this.groupDir);\r
37145             }\r
37146         }\r
37147     },\r
37148 \r
37149     // private\r
37150     applyGrouping : function(alwaysFireChange){\r
37151         if(this.groupField !== false){\r
37152             this.groupBy(this.groupField, true, this.groupDir);\r
37153             return true;\r
37154         }else{\r
37155             if(alwaysFireChange === true){\r
37156                 this.fireEvent('datachanged', this);\r
37157             }\r
37158             return false;\r
37159         }\r
37160     },\r
37161 \r
37162     // private\r
37163     getGroupState : function(){\r
37164         return this.groupOnSort && this.groupField !== false ?\r
37165                (this.sortInfo ? this.sortInfo.field : undefined) : this.groupField;\r
37166     }\r
37167 });\r
37168 Ext.reg('groupingstore', Ext.data.GroupingStore);/**\r
37169  * @class Ext.data.DirectProxy\r
37170  * @extends Ext.data.DataProxy\r
37171  */\r
37172 Ext.data.DirectProxy = function(config){\r
37173     Ext.apply(this, config);\r
37174     if(typeof this.paramOrder == 'string'){\r
37175         this.paramOrder = this.paramOrder.split(/[\s,|]/);\r
37176     }\r
37177     Ext.data.DirectProxy.superclass.constructor.call(this, config);\r
37178 };\r
37179 \r
37180 Ext.extend(Ext.data.DirectProxy, Ext.data.DataProxy, {\r
37181     /**\r
37182      * @cfg {Array/String} paramOrder Defaults to <tt>undefined</tt>. A list of params to be executed\r
37183      * server side.  Specify the params in the order in which they must be executed on the server-side\r
37184      * as either (1) an Array of String values, or (2) a String of params delimited by either whitespace,\r
37185      * comma, or pipe. For example,\r
37186      * any of the following would be acceptable:<pre><code>\r
37187 paramOrder: ['param1','param2','param3']\r
37188 paramOrder: 'param1 param2 param3'\r
37189 paramOrder: 'param1,param2,param3'\r
37190 paramOrder: 'param1|param2|param'\r
37191      </code></pre>\r
37192      */\r
37193     paramOrder: undefined,\r
37194 \r
37195     /**\r
37196      * @cfg {Boolean} paramsAsHash\r
37197      * Send parameters as a collection of named arguments (defaults to <tt>true</tt>). Providing a\r
37198      * <tt>{@link #paramOrder}</tt> nullifies this configuration.\r
37199      */\r
37200     paramsAsHash: true,\r
37201 \r
37202     /**\r
37203      * @cfg {Function} directFn\r
37204      * Function to call when executing a request.  directFn is a simple alternative to defining the api configuration-parameter\r
37205      * for Store's which will not implement a full CRUD api.\r
37206      */\r
37207     directFn : undefined,\r
37208 \r
37209     /**\r
37210      * DirectProxy implementation of {@link Ext.data.DataProxy#doRequest}\r
37211      * @param {String} action The crud action type (create, read, update, destroy)\r
37212      * @param {Ext.data.Record/Ext.data.Record[]} rs If action is load, rs will be null\r
37213      * @param {Object} params An object containing properties which are to be used as HTTP parameters\r
37214      * for the request to the remote server.\r
37215      * @param {Ext.data.DataReader} reader The Reader object which converts the data\r
37216      * object into a block of Ext.data.Records.\r
37217      * @param {Function} callback\r
37218      * <div class="sub-desc"><p>A function to be called after the request.\r
37219      * The <tt>callback</tt> is passed the following arguments:<ul>\r
37220      * <li><tt>r</tt> : Ext.data.Record[] The block of Ext.data.Records.</li>\r
37221      * <li><tt>options</tt>: Options object from the action request</li>\r
37222      * <li><tt>success</tt>: Boolean success indicator</li></ul></p></div>\r
37223      * @param {Object} scope The scope (<code>this</code> reference) in which the callback function is executed. Defaults to the browser window.\r
37224      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.\r
37225      * @protected\r
37226      */\r
37227     doRequest : function(action, rs, params, reader, callback, scope, options) {\r
37228         var args = [],\r
37229             directFn = this.api[action] || this.directFn;\r
37230 \r
37231         switch (action) {\r
37232             case Ext.data.Api.actions.create:\r
37233                 args.push(params.jsonData);             // <-- create(Hash)\r
37234                 break;\r
37235             case Ext.data.Api.actions.read:\r
37236                 // If the method has no parameters, ignore the paramOrder/paramsAsHash.\r
37237                 if(directFn.directCfg.method.len > 0){\r
37238                     if(this.paramOrder){\r
37239                         for(var i = 0, len = this.paramOrder.length; i < len; i++){\r
37240                             args.push(params[this.paramOrder[i]]);\r
37241                         }\r
37242                     }else if(this.paramsAsHash){\r
37243                         args.push(params);\r
37244                     }\r
37245                 }\r
37246                 break;\r
37247             case Ext.data.Api.actions.update:\r
37248                 args.push(params.jsonData);        // <-- update(Hash/Hash[])\r
37249                 break;\r
37250             case Ext.data.Api.actions.destroy:\r
37251                 args.push(params.jsonData);        // <-- destroy(Int/Int[])\r
37252                 break;\r
37253         }\r
37254 \r
37255         var trans = {\r
37256             params : params || {},\r
37257             request: {\r
37258                 callback : callback,\r
37259                 scope : scope,\r
37260                 arg : options\r
37261             },\r
37262             reader: reader\r
37263         };\r
37264 \r
37265         args.push(this.createCallback(action, rs, trans), this);\r
37266         directFn.apply(window, args);\r
37267     },\r
37268 \r
37269     // private\r
37270     createCallback : function(action, rs, trans) {\r
37271         return function(result, res) {\r
37272             if (!res.status) {\r
37273                 // @deprecated fire loadexception\r
37274                 if (action === Ext.data.Api.actions.read) {\r
37275                     this.fireEvent("loadexception", this, trans, res, null);\r
37276                 }\r
37277                 this.fireEvent('exception', this, 'remote', action, trans, res, null);\r
37278                 trans.request.callback.call(trans.request.scope, null, trans.request.arg, false);\r
37279                 return;\r
37280             }\r
37281             if (action === Ext.data.Api.actions.read) {\r
37282                 this.onRead(action, trans, result, res);\r
37283             } else {\r
37284                 this.onWrite(action, trans, result, res, rs);\r
37285             }\r
37286         };\r
37287     },\r
37288     /**\r
37289      * Callback for read actions\r
37290      * @param {String} action [Ext.data.Api.actions.create|read|update|destroy]\r
37291      * @param {Object} trans The request transaction object\r
37292      * @param {Object} result Data object picked out of the server-response.\r
37293      * @param {Object} res The server response\r
37294      * @protected\r
37295      */\r
37296     onRead : function(action, trans, result, res) {\r
37297         var records;\r
37298         try {\r
37299             records = trans.reader.readRecords(result);\r
37300         }\r
37301         catch (ex) {\r
37302             // @deprecated: Fire old loadexception for backwards-compat.\r
37303             this.fireEvent("loadexception", this, trans, res, ex);\r
37304 \r
37305             this.fireEvent('exception', this, 'response', action, trans, res, ex);\r
37306             trans.request.callback.call(trans.request.scope, null, trans.request.arg, false);\r
37307             return;\r
37308         }\r
37309         this.fireEvent("load", this, res, trans.request.arg);\r
37310         trans.request.callback.call(trans.request.scope, records, trans.request.arg, true);\r
37311     },\r
37312     /**\r
37313      * Callback for write actions\r
37314      * @param {String} action [{@link Ext.data.Api#actions create|read|update|destroy}]\r
37315      * @param {Object} trans The request transaction object\r
37316      * @param {Object} result Data object picked out of the server-response.\r
37317      * @param {Object} res The server response\r
37318      * @param {Ext.data.Record/[Ext.data.Record]} rs The Store resultset associated with the action.\r
37319      * @protected\r
37320      */\r
37321     onWrite : function(action, trans, result, res, rs) {\r
37322         var data = trans.reader.extractData(result, false);\r
37323         this.fireEvent("write", this, action, data, res, rs, trans.request.arg);\r
37324         trans.request.callback.call(trans.request.scope, data, res, true);\r
37325     }\r
37326 });\r
37327 \r
37328 /**\r
37329  * @class Ext.data.DirectStore\r
37330  * @extends Ext.data.Store\r
37331  * <p>Small helper class to create an {@link Ext.data.Store} configured with an\r
37332  * {@link Ext.data.DirectProxy} and {@link Ext.data.JsonReader} to make interacting\r
37333  * with an {@link Ext.Direct} Server-side {@link Ext.direct.Provider Provider} easier.\r
37334  * To create a different proxy/reader combination create a basic {@link Ext.data.Store}\r
37335  * configured as needed.</p>\r
37336  *\r
37337  * <p><b>*Note:</b> Although they are not listed, this class inherits all of the config options of:</p>\r
37338  * <div><ul class="mdetail-params">\r
37339  * <li><b>{@link Ext.data.Store Store}</b></li>\r
37340  * <div class="sub-desc"><ul class="mdetail-params">\r
37341  *\r
37342  * </ul></div>\r
37343  * <li><b>{@link Ext.data.JsonReader JsonReader}</b></li>\r
37344  * <div class="sub-desc"><ul class="mdetail-params">\r
37345  * <li><tt><b>{@link Ext.data.JsonReader#root root}</b></tt></li>\r
37346  * <li><tt><b>{@link Ext.data.JsonReader#idProperty idProperty}</b></tt></li>\r
37347  * <li><tt><b>{@link Ext.data.JsonReader#totalProperty totalProperty}</b></tt></li>\r
37348  * </ul></div>\r
37349  *\r
37350  * <li><b>{@link Ext.data.DirectProxy DirectProxy}</b></li>\r
37351  * <div class="sub-desc"><ul class="mdetail-params">\r
37352  * <li><tt><b>{@link Ext.data.DirectProxy#directFn directFn}</b></tt></li>\r
37353  * <li><tt><b>{@link Ext.data.DirectProxy#paramOrder paramOrder}</b></tt></li>\r
37354  * <li><tt><b>{@link Ext.data.DirectProxy#paramsAsHash paramsAsHash}</b></tt></li>\r
37355  * </ul></div>\r
37356  * </ul></div>\r
37357  *\r
37358  * @xtype directstore\r
37359  *\r
37360  * @constructor\r
37361  * @param {Object} config\r
37362  */\r
37363 Ext.data.DirectStore = Ext.extend(Ext.data.Store, {\r
37364     constructor : function(config){\r
37365         // each transaction upon a singe record will generate a distinct Direct transaction since Direct queues them into one Ajax request.\r
37366         var c = Ext.apply({}, {\r
37367             batchTransactions: false\r
37368         }, config);\r
37369         Ext.data.DirectStore.superclass.constructor.call(this, Ext.apply(c, {\r
37370             proxy: Ext.isDefined(c.proxy) ? c.proxy : new Ext.data.DirectProxy(Ext.copyTo({}, c, 'paramOrder,paramsAsHash,directFn,api')),\r
37371             reader: (!Ext.isDefined(c.reader) && c.fields) ? new Ext.data.JsonReader(Ext.copyTo({}, c, 'totalProperty,root,idProperty'), c.fields) : c.reader\r
37372         }));\r
37373     }\r
37374 });\r
37375 Ext.reg('directstore', Ext.data.DirectStore);
37376 /**\r
37377  * @class Ext.Direct\r
37378  * @extends Ext.util.Observable\r
37379  * <p><b><u>Overview</u></b></p>\r
37380  * \r
37381  * <p>Ext.Direct aims to streamline communication between the client and server\r
37382  * by providing a single interface that reduces the amount of common code\r
37383  * typically required to validate data and handle returned data packets\r
37384  * (reading data, error conditions, etc).</p>\r
37385  *  \r
37386  * <p>The Ext.direct namespace includes several classes for a closer integration\r
37387  * with the server-side. The Ext.data namespace also includes classes for working\r
37388  * with Ext.data.Stores which are backed by data from an Ext.Direct method.</p>\r
37389  * \r
37390  * <p><b><u>Specification</u></b></p>\r
37391  * \r
37392  * <p>For additional information consult the \r
37393  * <a href="http://extjs.com/products/extjs/direct.php">Ext.Direct Specification</a>.</p>\r
37394  *   \r
37395  * <p><b><u>Providers</u></b></p>\r
37396  * \r
37397  * <p>Ext.Direct uses a provider architecture, where one or more providers are\r
37398  * used to transport data to and from the server. There are several providers\r
37399  * that exist in the core at the moment:</p><div class="mdetail-params"><ul>\r
37400  * \r
37401  * <li>{@link Ext.direct.JsonProvider JsonProvider} for simple JSON operations</li>\r
37402  * <li>{@link Ext.direct.PollingProvider PollingProvider} for repeated requests</li>\r
37403  * <li>{@link Ext.direct.RemotingProvider RemotingProvider} exposes server side\r
37404  * on the client.</li>\r
37405  * </ul></div>\r
37406  * \r
37407  * <p>A provider does not need to be invoked directly, providers are added via\r
37408  * {@link Ext.Direct}.{@link Ext.Direct#add add}.</p>\r
37409  * \r
37410  * <p><b><u>Router</u></b></p>\r
37411  * \r
37412  * <p>Ext.Direct utilizes a "router" on the server to direct requests from the client\r
37413  * to the appropriate server-side method. Because the Ext.Direct API is completely\r
37414  * platform-agnostic, you could completely swap out a Java based server solution\r
37415  * and replace it with one that uses C# without changing the client side JavaScript\r
37416  * at all.</p>\r
37417  * \r
37418  * <p><b><u>Server side events</u></b></p>\r
37419  * \r
37420  * <p>Custom events from the server may be handled by the client by adding\r
37421  * listeners, for example:</p>\r
37422  * <pre><code>\r
37423 {"type":"event","name":"message","data":"Successfully polled at: 11:19:30 am"}\r
37424 \r
37425 // add a handler for a 'message' event sent by the server \r
37426 Ext.Direct.on('message', function(e){\r
37427     out.append(String.format('&lt;p>&lt;i>{0}&lt;/i>&lt;/p>', e.data));\r
37428             out.el.scrollTo('t', 100000, true);\r
37429 });\r
37430  * </code></pre>\r
37431  * @singleton\r
37432  */\r
37433 Ext.Direct = Ext.extend(Ext.util.Observable, {\r
37434     /**\r
37435      * Each event type implements a getData() method. The default event types are:\r
37436      * <div class="mdetail-params"><ul>\r
37437      * <li><b><tt>event</tt></b> : Ext.Direct.Event</li>\r
37438      * <li><b><tt>exception</tt></b> : Ext.Direct.ExceptionEvent</li>\r
37439      * <li><b><tt>rpc</tt></b> : Ext.Direct.RemotingEvent</li>\r
37440      * </ul></div>\r
37441      * @property eventTypes\r
37442      * @type Object\r
37443      */\r
37444 \r
37445     /**\r
37446      * Four types of possible exceptions which can occur:\r
37447      * <div class="mdetail-params"><ul>\r
37448      * <li><b><tt>Ext.Direct.exceptions.TRANSPORT</tt></b> : 'xhr'</li>\r
37449      * <li><b><tt>Ext.Direct.exceptions.PARSE</tt></b> : 'parse'</li>\r
37450      * <li><b><tt>Ext.Direct.exceptions.LOGIN</tt></b> : 'login'</li>\r
37451      * <li><b><tt>Ext.Direct.exceptions.SERVER</tt></b> : 'exception'</li>\r
37452      * </ul></div>\r
37453      * @property exceptions\r
37454      * @type Object\r
37455      */\r
37456     exceptions: {\r
37457         TRANSPORT: 'xhr',\r
37458         PARSE: 'parse',\r
37459         LOGIN: 'login',\r
37460         SERVER: 'exception'\r
37461     },\r
37462     \r
37463     // private\r
37464     constructor: function(){\r
37465         this.addEvents(\r
37466             /**\r
37467              * @event event\r
37468              * Fires after an event.\r
37469              * @param {event} e The {@link Ext.Direct#eventTypes Ext.Direct.Event type} that occurred.\r
37470              * @param {Ext.direct.Provider} provider The {@link Ext.direct.Provider Provider}.\r
37471              */\r
37472             'event',\r
37473             /**\r
37474              * @event exception\r
37475              * Fires after an event exception.\r
37476              * @param {event} e The {@link Ext.Direct#eventTypes Ext.Direct.Event type} that occurred.\r
37477              */\r
37478             'exception'\r
37479         );\r
37480         this.transactions = {};\r
37481         this.providers = {};\r
37482     },\r
37483 \r
37484     /**\r
37485      * Adds an Ext.Direct Provider and creates the proxy or stub methods to execute server-side methods.\r
37486      * If the provider is not already connected, it will auto-connect.\r
37487      * <pre><code>\r
37488 var pollProv = new Ext.direct.PollingProvider({\r
37489     url: 'php/poll2.php'\r
37490 }); \r
37491 \r
37492 Ext.Direct.addProvider(\r
37493     {\r
37494         "type":"remoting",       // create a {@link Ext.direct.RemotingProvider} \r
37495         "url":"php\/router.php", // url to connect to the Ext.Direct server-side router.\r
37496         "actions":{              // each property within the actions object represents a Class \r
37497             "TestAction":[       // array of methods within each server side Class   \r
37498             {\r
37499                 "name":"doEcho", // name of method\r
37500                 "len":1\r
37501             },{\r
37502                 "name":"multiply",\r
37503                 "len":1\r
37504             },{\r
37505                 "name":"doForm",\r
37506                 "formHandler":true, // handle form on server with Ext.Direct.Transaction \r
37507                 "len":1\r
37508             }]\r
37509         },\r
37510         "namespace":"myApplication",// namespace to create the Remoting Provider in\r
37511     },{\r
37512         type: 'polling', // create a {@link Ext.direct.PollingProvider} \r
37513         url:  'php/poll.php'\r
37514     },\r
37515     pollProv // reference to previously created instance\r
37516 );\r
37517      * </code></pre>\r
37518      * @param {Object/Array} provider Accepts either an Array of Provider descriptions (an instance\r
37519      * or config object for a Provider) or any number of Provider descriptions as arguments.  Each\r
37520      * Provider description instructs Ext.Direct how to create client-side stub methods.\r
37521      */\r
37522     addProvider : function(provider){        \r
37523         var a = arguments;\r
37524         if(a.length > 1){\r
37525             for(var i = 0, len = a.length; i < len; i++){\r
37526                 this.addProvider(a[i]);\r
37527             }\r
37528             return;\r
37529         }\r
37530         \r
37531         // if provider has not already been instantiated\r
37532         if(!provider.events){\r
37533             provider = new Ext.Direct.PROVIDERS[provider.type](provider);\r
37534         }\r
37535         provider.id = provider.id || Ext.id();\r
37536         this.providers[provider.id] = provider;\r
37537 \r
37538         provider.on('data', this.onProviderData, this);\r
37539         provider.on('exception', this.onProviderException, this);\r
37540 \r
37541 \r
37542         if(!provider.isConnected()){\r
37543             provider.connect();\r
37544         }\r
37545 \r
37546         return provider;\r
37547     },\r
37548 \r
37549     /**\r
37550      * Retrieve a {@link Ext.direct.Provider provider} by the\r
37551      * <b><tt>{@link Ext.direct.Provider#id id}</tt></b> specified when the provider is\r
37552      * {@link #addProvider added}.\r
37553      * @param {String} id Unique identifier assigned to the provider when calling {@link #addProvider} \r
37554      */\r
37555     getProvider : function(id){\r
37556         return this.providers[id];\r
37557     },\r
37558 \r
37559     removeProvider : function(id){\r
37560         var provider = id.id ? id : this.providers[id.id];\r
37561         provider.un('data', this.onProviderData, this);\r
37562         provider.un('exception', this.onProviderException, this);\r
37563         delete this.providers[provider.id];\r
37564         return provider;\r
37565     },\r
37566 \r
37567     addTransaction: function(t){\r
37568         this.transactions[t.tid] = t;\r
37569         return t;\r
37570     },\r
37571 \r
37572     removeTransaction: function(t){\r
37573         delete this.transactions[t.tid || t];\r
37574         return t;\r
37575     },\r
37576 \r
37577     getTransaction: function(tid){\r
37578         return this.transactions[tid.tid || tid];\r
37579     },\r
37580 \r
37581     onProviderData : function(provider, e){\r
37582         if(Ext.isArray(e)){\r
37583             for(var i = 0, len = e.length; i < len; i++){\r
37584                 this.onProviderData(provider, e[i]);\r
37585             }\r
37586             return;\r
37587         }\r
37588         if(e.name && e.name != 'event' && e.name != 'exception'){\r
37589             this.fireEvent(e.name, e);\r
37590         }else if(e.type == 'exception'){\r
37591             this.fireEvent('exception', e);\r
37592         }\r
37593         this.fireEvent('event', e, provider);\r
37594     },\r
37595 \r
37596     createEvent : function(response, extraProps){\r
37597         return new Ext.Direct.eventTypes[response.type](Ext.apply(response, extraProps));\r
37598     }\r
37599 });\r
37600 // overwrite impl. with static instance\r
37601 Ext.Direct = new Ext.Direct();\r
37602 \r
37603 Ext.Direct.TID = 1;\r
37604 Ext.Direct.PROVIDERS = {};/**\r
37605  * @class Ext.Direct.Transaction\r
37606  * @extends Object\r
37607  * <p>Supporting Class for Ext.Direct (not intended to be used directly).</p>\r
37608  * @constructor\r
37609  * @param {Object} config\r
37610  */\r
37611 Ext.Direct.Transaction = function(config){\r
37612     Ext.apply(this, config);\r
37613     this.tid = ++Ext.Direct.TID;\r
37614     this.retryCount = 0;\r
37615 };\r
37616 Ext.Direct.Transaction.prototype = {\r
37617     send: function(){\r
37618         this.provider.queueTransaction(this);\r
37619     },\r
37620 \r
37621     retry: function(){\r
37622         this.retryCount++;\r
37623         this.send();\r
37624     },\r
37625 \r
37626     getProvider: function(){\r
37627         return this.provider;\r
37628     }\r
37629 };Ext.Direct.Event = function(config){\r
37630     Ext.apply(this, config);\r
37631 }\r
37632 Ext.Direct.Event.prototype = {\r
37633     status: true,\r
37634     getData: function(){\r
37635         return this.data;\r
37636     }\r
37637 };\r
37638 \r
37639 Ext.Direct.RemotingEvent = Ext.extend(Ext.Direct.Event, {\r
37640     type: 'rpc',\r
37641     getTransaction: function(){\r
37642         return this.transaction || Ext.Direct.getTransaction(this.tid);\r
37643     }\r
37644 });\r
37645 \r
37646 Ext.Direct.ExceptionEvent = Ext.extend(Ext.Direct.RemotingEvent, {\r
37647     status: false,\r
37648     type: 'exception'\r
37649 });\r
37650 \r
37651 Ext.Direct.eventTypes = {\r
37652     'rpc':  Ext.Direct.RemotingEvent,\r
37653     'event':  Ext.Direct.Event,\r
37654     'exception':  Ext.Direct.ExceptionEvent\r
37655 };\r
37656 \r
37657 /**\r
37658  * @class Ext.direct.Provider\r
37659  * @extends Ext.util.Observable\r
37660  * <p>Ext.direct.Provider is an abstract class meant to be extended.</p>\r
37661  * \r
37662  * <p>For example ExtJs implements the following subclasses:</p>\r
37663  * <pre><code>\r
37664 Provider\r
37665 |\r
37666 +---{@link Ext.direct.JsonProvider JsonProvider} \r
37667     |\r
37668     +---{@link Ext.direct.PollingProvider PollingProvider}   \r
37669     |\r
37670     +---{@link Ext.direct.RemotingProvider RemotingProvider}   \r
37671  * </code></pre>\r
37672  * @abstract\r
37673  */\r
37674 Ext.direct.Provider = Ext.extend(Ext.util.Observable, {    \r
37675     /**\r
37676      * @cfg {String} id\r
37677      * The unique id of the provider (defaults to an {@link Ext#id auto-assigned id}).\r
37678      * You should assign an id if you need to be able to access the provider later and you do\r
37679      * not have an object reference available, for example:\r
37680      * <pre><code>\r
37681 Ext.Direct.addProvider(\r
37682     {\r
37683         type: 'polling',\r
37684         url:  'php/poll.php',\r
37685         id:   'poll-provider'\r
37686     }\r
37687 );\r
37688      \r
37689 var p = {@link Ext.Direct Ext.Direct}.{@link Ext.Direct#getProvider getProvider}('poll-provider');\r
37690 p.disconnect();\r
37691      * </code></pre>\r
37692      */\r
37693         \r
37694     /**\r
37695      * @cfg {Number} priority\r
37696      * Priority of the request. Lower is higher priority, <tt>0</tt> means "duplex" (always on).\r
37697      * All Providers default to <tt>1</tt> except for PollingProvider which defaults to <tt>3</tt>.\r
37698      */    \r
37699     priority: 1,\r
37700 \r
37701     /**\r
37702      * @cfg {String} type\r
37703      * <b>Required</b>, <tt>undefined</tt> by default.  The <tt>type</tt> of provider specified\r
37704      * to {@link Ext.Direct Ext.Direct}.{@link Ext.Direct#addProvider addProvider} to create a\r
37705      * new Provider. Acceptable values by default are:<div class="mdetail-params"><ul>\r
37706      * <li><b><tt>polling</tt></b> : {@link Ext.direct.PollingProvider PollingProvider}</li>\r
37707      * <li><b><tt>remoting</tt></b> : {@link Ext.direct.RemotingProvider RemotingProvider}</li>\r
37708      * </ul></div>\r
37709      */    \r
37710  \r
37711     // private\r
37712     constructor : function(config){\r
37713         Ext.apply(this, config);\r
37714         this.addEvents(\r
37715             /**\r
37716              * @event connect\r
37717              * Fires when the Provider connects to the server-side\r
37718              * @param {Ext.direct.Provider} provider The {@link Ext.direct.Provider Provider}.\r
37719              */            \r
37720             'connect',\r
37721             /**\r
37722              * @event disconnect\r
37723              * Fires when the Provider disconnects from the server-side\r
37724              * @param {Ext.direct.Provider} provider The {@link Ext.direct.Provider Provider}.\r
37725              */            \r
37726             'disconnect',\r
37727             /**\r
37728              * @event data\r
37729              * Fires when the Provider receives data from the server-side\r
37730              * @param {Ext.direct.Provider} provider The {@link Ext.direct.Provider Provider}.\r
37731              * @param {event} e The {@link Ext.Direct#eventTypes Ext.Direct.Event type} that occurred.\r
37732              */            \r
37733             'data',\r
37734             /**\r
37735              * @event exception\r
37736              * Fires when the Provider receives an exception from the server-side\r
37737              */                        \r
37738             'exception'\r
37739         );\r
37740         Ext.direct.Provider.superclass.constructor.call(this, config);\r
37741     },\r
37742 \r
37743     /**\r
37744      * Returns whether or not the server-side is currently connected.\r
37745      * Abstract method for subclasses to implement.\r
37746      */\r
37747     isConnected: function(){\r
37748         return false;\r
37749     },\r
37750 \r
37751     /**\r
37752      * Abstract methods for subclasses to implement.\r
37753      */\r
37754     connect: Ext.emptyFn,\r
37755     \r
37756     /**\r
37757      * Abstract methods for subclasses to implement.\r
37758      */\r
37759     disconnect: Ext.emptyFn\r
37760 });\r
37761 /**\r
37762  * @class Ext.direct.JsonProvider\r
37763  * @extends Ext.direct.Provider\r
37764  */\r
37765 Ext.direct.JsonProvider = Ext.extend(Ext.direct.Provider, {\r
37766     parseResponse: function(xhr){\r
37767         if(!Ext.isEmpty(xhr.responseText)){\r
37768             if(typeof xhr.responseText == 'object'){\r
37769                 return xhr.responseText;\r
37770             }\r
37771             return Ext.decode(xhr.responseText);\r
37772         }\r
37773         return null;\r
37774     },\r
37775 \r
37776     getEvents: function(xhr){\r
37777         var data = null;\r
37778         try{\r
37779             data = this.parseResponse(xhr);\r
37780         }catch(e){\r
37781             var event = new Ext.Direct.ExceptionEvent({\r
37782                 data: e,\r
37783                 xhr: xhr,\r
37784                 code: Ext.Direct.exceptions.PARSE,\r
37785                 message: 'Error parsing json response: \n\n ' + data\r
37786             })\r
37787             return [event];\r
37788         }\r
37789         var events = [];\r
37790         if(Ext.isArray(data)){\r
37791             for(var i = 0, len = data.length; i < len; i++){\r
37792                 events.push(Ext.Direct.createEvent(data[i]));\r
37793             }\r
37794         }else{\r
37795             events.push(Ext.Direct.createEvent(data));\r
37796         }\r
37797         return events;\r
37798     }\r
37799 });/**\r
37800  * @class Ext.direct.PollingProvider\r
37801  * @extends Ext.direct.JsonProvider\r
37802  *\r
37803  * <p>Provides for repetitive polling of the server at distinct {@link #interval intervals}.\r
37804  * The initial request for data originates from the client, and then is responded to by the\r
37805  * server.</p>\r
37806  * \r
37807  * <p>All configurations for the PollingProvider should be generated by the server-side\r
37808  * API portion of the Ext.Direct stack.</p>\r
37809  *\r
37810  * <p>An instance of PollingProvider may be created directly via the new keyword or by simply\r
37811  * specifying <tt>type = 'polling'</tt>.  For example:</p>\r
37812  * <pre><code>\r
37813 var pollA = new Ext.direct.PollingProvider({\r
37814     type:'polling',\r
37815     url: 'php/pollA.php',\r
37816 });\r
37817 Ext.Direct.addProvider(pollA);\r
37818 pollA.disconnect();\r
37819 \r
37820 Ext.Direct.addProvider(\r
37821     {\r
37822         type:'polling',\r
37823         url: 'php/pollB.php',\r
37824         id: 'pollB-provider'\r
37825     }\r
37826 );\r
37827 var pollB = Ext.Direct.getProvider('pollB-provider');\r
37828  * </code></pre>\r
37829  */\r
37830 Ext.direct.PollingProvider = Ext.extend(Ext.direct.JsonProvider, {\r
37831     /**\r
37832      * @cfg {Number} priority\r
37833      * Priority of the request (defaults to <tt>3</tt>). See {@link Ext.direct.Provider#priority}.\r
37834      */\r
37835     // override default priority\r
37836     priority: 3,\r
37837     \r
37838     /**\r
37839      * @cfg {Number} interval\r
37840      * How often to poll the server-side in milliseconds (defaults to <tt>3000</tt> - every\r
37841      * 3 seconds).\r
37842      */\r
37843     interval: 3000,\r
37844 \r
37845     /**\r
37846      * @cfg {Object} baseParams An object containing properties which are to be sent as parameters\r
37847      * on every polling request\r
37848      */\r
37849     \r
37850     /**\r
37851      * @cfg {String/Function} url\r
37852      * The url which the PollingProvider should contact with each request. This can also be\r
37853      * an imported Ext.Direct method which will accept the baseParams as its only argument.\r
37854      */\r
37855 \r
37856     // private\r
37857     constructor : function(config){\r
37858         Ext.direct.PollingProvider.superclass.constructor.call(this, config);\r
37859         this.addEvents(\r
37860             /**\r
37861              * @event beforepoll\r
37862              * Fired immediately before a poll takes place, an event handler can return false\r
37863              * in order to cancel the poll.\r
37864              * @param {Ext.direct.PollingProvider}\r
37865              */\r
37866             'beforepoll',            \r
37867             /**\r
37868              * @event poll\r
37869              * This event has not yet been implemented.\r
37870              * @param {Ext.direct.PollingProvider}\r
37871              */\r
37872             'poll'\r
37873         );\r
37874     },\r
37875 \r
37876     // inherited\r
37877     isConnected: function(){\r
37878         return !!this.pollTask;\r
37879     },\r
37880 \r
37881     /**\r
37882      * Connect to the server-side and begin the polling process. To handle each\r
37883      * response subscribe to the data event.\r
37884      */\r
37885     connect: function(){\r
37886         if(this.url && !this.pollTask){\r
37887             this.pollTask = Ext.TaskMgr.start({\r
37888                 run: function(){\r
37889                     if(this.fireEvent('beforepoll', this) !== false){\r
37890                         if(typeof this.url == 'function'){\r
37891                             this.url(this.baseParams);\r
37892                         }else{\r
37893                             Ext.Ajax.request({\r
37894                                 url: this.url,\r
37895                                 callback: this.onData,\r
37896                                 scope: this,\r
37897                                 params: this.baseParams\r
37898                             });\r
37899                         }\r
37900                     }\r
37901                 },\r
37902                 interval: this.interval,\r
37903                 scope: this\r
37904             });\r
37905             this.fireEvent('connect', this);\r
37906         }else if(!this.url){\r
37907             throw 'Error initializing PollingProvider, no url configured.';\r
37908         }\r
37909     },\r
37910 \r
37911     /**\r
37912      * Disconnect from the server-side and stop the polling process. The disconnect\r
37913      * event will be fired on a successful disconnect.\r
37914      */\r
37915     disconnect: function(){\r
37916         if(this.pollTask){\r
37917             Ext.TaskMgr.stop(this.pollTask);\r
37918             delete this.pollTask;\r
37919             this.fireEvent('disconnect', this);\r
37920         }\r
37921     },\r
37922 \r
37923     // private\r
37924     onData: function(opt, success, xhr){\r
37925         if(success){\r
37926             var events = this.getEvents(xhr);\r
37927             for(var i = 0, len = events.length; i < len; i++){\r
37928                 var e = events[i];\r
37929                 this.fireEvent('data', this, e);\r
37930             }\r
37931         }else{\r
37932             var e = new Ext.Direct.ExceptionEvent({\r
37933                 data: e,\r
37934                 code: Ext.Direct.exceptions.TRANSPORT,\r
37935                 message: 'Unable to connect to the server.',\r
37936                 xhr: xhr\r
37937             });\r
37938             this.fireEvent('data', this, e);\r
37939         }\r
37940     }\r
37941 });\r
37942 \r
37943 Ext.Direct.PROVIDERS['polling'] = Ext.direct.PollingProvider;/**\r
37944  * @class Ext.direct.RemotingProvider\r
37945  * @extends Ext.direct.JsonProvider\r
37946  * \r
37947  * <p>The {@link Ext.direct.RemotingProvider RemotingProvider} exposes access to\r
37948  * server side methods on the client (a remote procedure call (RPC) type of\r
37949  * connection where the client can initiate a procedure on the server).</p>\r
37950  * \r
37951  * <p>This allows for code to be organized in a fashion that is maintainable,\r
37952  * while providing a clear path between client and server, something that is\r
37953  * not always apparent when using URLs.</p>\r
37954  * \r
37955  * <p>To accomplish this the server-side needs to describe what classes and methods\r
37956  * are available on the client-side. This configuration will typically be\r
37957  * outputted by the server-side Ext.Direct stack when the API description is built.</p>\r
37958  */\r
37959 Ext.direct.RemotingProvider = Ext.extend(Ext.direct.JsonProvider, {       \r
37960     /**\r
37961      * @cfg {Object} actions\r
37962      * Object literal defining the server side actions and methods. For example, if\r
37963      * the Provider is configured with:\r
37964      * <pre><code>\r
37965 "actions":{ // each property within the 'actions' object represents a server side Class \r
37966     "TestAction":[ // array of methods within each server side Class to be   \r
37967     {              // stubbed out on client\r
37968         "name":"doEcho", \r
37969         "len":1            \r
37970     },{\r
37971         "name":"multiply",// name of method\r
37972         "len":2           // The number of parameters that will be used to create an\r
37973                           // array of data to send to the server side function.\r
37974                           // Ensure the server sends back a Number, not a String. \r
37975     },{\r
37976         "name":"doForm",\r
37977         "formHandler":true, // direct the client to use specialized form handling method \r
37978         "len":1\r
37979     }]\r
37980 }\r
37981      * </code></pre>\r
37982      * <p>Note that a Store is not required, a server method can be called at any time.\r
37983      * In the following example a <b>client side</b> handler is used to call the\r
37984      * server side method "multiply" in the server-side "TestAction" Class:</p>\r
37985      * <pre><code>\r
37986 TestAction.multiply(\r
37987     2, 4, // pass two arguments to server, so specify len=2\r
37988     // callback function after the server is called\r
37989     // result: the result returned by the server\r
37990     //      e: Ext.Direct.RemotingEvent object\r
37991     function(result, e){\r
37992         var t = e.getTransaction();\r
37993         var action = t.action; // server side Class called\r
37994         var method = t.method; // server side method called\r
37995         if(e.status){\r
37996             var answer = Ext.encode(result); // 8\r
37997     \r
37998         }else{\r
37999             var msg = e.message; // failure message\r
38000         }\r
38001     }\r
38002 );\r
38003      * </code></pre>\r
38004      * In the example above, the server side "multiply" function will be passed two\r
38005      * arguments (2 and 4).  The "multiply" method should return the value 8 which will be\r
38006      * available as the <tt>result</tt> in the example above. \r
38007      */\r
38008     \r
38009     /**\r
38010      * @cfg {String/Object} namespace\r
38011      * Namespace for the Remoting Provider (defaults to the browser global scope of <i>window</i>).\r
38012      * Explicitly specify the namespace Object, or specify a String to have a\r
38013      * {@link Ext#namespace namespace created} implicitly.\r
38014      */\r
38015     \r
38016     /**\r
38017      * @cfg {String} url\r
38018      * <b>Required<b>. The url to connect to the {@link Ext.Direct} server-side router. \r
38019      */\r
38020     \r
38021     /**\r
38022      * @cfg {String} enableUrlEncode\r
38023      * Specify which param will hold the arguments for the method.\r
38024      * Defaults to <tt>'data'</tt>.\r
38025      */\r
38026     \r
38027     /**\r
38028      * @cfg {Number/Boolean} enableBuffer\r
38029      * <p><tt>true</tt> or <tt>false</tt> to enable or disable combining of method\r
38030      * calls. If a number is specified this is the amount of time in milliseconds\r
38031      * to wait before sending a batched request (defaults to <tt>10</tt>).</p>\r
38032      * <br><p>Calls which are received within the specified timeframe will be\r
38033      * concatenated together and sent in a single request, optimizing the\r
38034      * application by reducing the amount of round trips that have to be made\r
38035      * to the server.</p>\r
38036      */\r
38037     enableBuffer: 10,\r
38038     \r
38039     /**\r
38040      * @cfg {Number} maxRetries\r
38041      * Number of times to re-attempt delivery on failure of a call. Defaults to <tt>1</tt>.\r
38042      */\r
38043     maxRetries: 1,\r
38044     \r
38045     /**\r
38046      * @cfg {Number} timeout\r
38047      * The timeout to use for each request. Defaults to <tt>undefined</tt>.\r
38048      */\r
38049     timeout: undefined,\r
38050 \r
38051     constructor : function(config){\r
38052         Ext.direct.RemotingProvider.superclass.constructor.call(this, config);\r
38053         this.addEvents(\r
38054             /**\r
38055              * @event beforecall\r
38056              * Fires immediately before the client-side sends off the RPC call.\r
38057              * By returning false from an event handler you can prevent the call from\r
38058              * executing.\r
38059              * @param {Ext.direct.RemotingProvider} provider\r
38060              * @param {Ext.Direct.Transaction} transaction\r
38061              */            \r
38062             'beforecall',            \r
38063             /**\r
38064              * @event call\r
38065              * Fires immediately after the request to the server-side is sent. This does\r
38066              * NOT fire after the response has come back from the call.\r
38067              * @param {Ext.direct.RemotingProvider} provider\r
38068              * @param {Ext.Direct.Transaction} transaction\r
38069              */            \r
38070             'call'\r
38071         );\r
38072         this.namespace = (Ext.isString(this.namespace)) ? Ext.ns(this.namespace) : this.namespace || window;\r
38073         this.transactions = {};\r
38074         this.callBuffer = [];\r
38075     },\r
38076 \r
38077     // private\r
38078     initAPI : function(){\r
38079         var o = this.actions;\r
38080         for(var c in o){\r
38081             var cls = this.namespace[c] || (this.namespace[c] = {}),\r
38082                 ms = o[c];\r
38083             for(var i = 0, len = ms.length; i < len; i++){\r
38084                 var m = ms[i];\r
38085                 cls[m.name] = this.createMethod(c, m);\r
38086             }\r
38087         }\r
38088     },\r
38089 \r
38090     // inherited\r
38091     isConnected: function(){\r
38092         return !!this.connected;\r
38093     },\r
38094 \r
38095     connect: function(){\r
38096         if(this.url){\r
38097             this.initAPI();\r
38098             this.connected = true;\r
38099             this.fireEvent('connect', this);\r
38100         }else if(!this.url){\r
38101             throw 'Error initializing RemotingProvider, no url configured.';\r
38102         }\r
38103     },\r
38104 \r
38105     disconnect: function(){\r
38106         if(this.connected){\r
38107             this.connected = false;\r
38108             this.fireEvent('disconnect', this);\r
38109         }\r
38110     },\r
38111 \r
38112     onData: function(opt, success, xhr){\r
38113         if(success){\r
38114             var events = this.getEvents(xhr);\r
38115             for(var i = 0, len = events.length; i < len; i++){\r
38116                 var e = events[i],\r
38117                     t = this.getTransaction(e);\r
38118                 this.fireEvent('data', this, e);\r
38119                 if(t){\r
38120                     this.doCallback(t, e, true);\r
38121                     Ext.Direct.removeTransaction(t);\r
38122                 }\r
38123             }\r
38124         }else{\r
38125             var ts = [].concat(opt.ts);\r
38126             for(var i = 0, len = ts.length; i < len; i++){\r
38127                 var t = this.getTransaction(ts[i]);\r
38128                 if(t && t.retryCount < this.maxRetries){\r
38129                     t.retry();\r
38130                 }else{\r
38131                     var e = new Ext.Direct.ExceptionEvent({\r
38132                         data: e,\r
38133                         transaction: t,\r
38134                         code: Ext.Direct.exceptions.TRANSPORT,\r
38135                         message: 'Unable to connect to the server.',\r
38136                         xhr: xhr\r
38137                     });\r
38138                     this.fireEvent('data', this, e);\r
38139                     if(t){\r
38140                         this.doCallback(t, e, false);\r
38141                         Ext.Direct.removeTransaction(t);\r
38142                     }\r
38143                 }\r
38144             }\r
38145         }\r
38146     },\r
38147 \r
38148     getCallData: function(t){\r
38149         return {\r
38150             action: t.action,\r
38151             method: t.method,\r
38152             data: t.data,\r
38153             type: 'rpc',\r
38154             tid: t.tid\r
38155         };\r
38156     },\r
38157 \r
38158     doSend : function(data){\r
38159         var o = {\r
38160             url: this.url,\r
38161             callback: this.onData,\r
38162             scope: this,\r
38163             ts: data,\r
38164             timeout: this.timeout\r
38165         }, callData;\r
38166 \r
38167         if(Ext.isArray(data)){\r
38168             callData = [];\r
38169             for(var i = 0, len = data.length; i < len; i++){\r
38170                 callData.push(this.getCallData(data[i]));\r
38171             }\r
38172         }else{\r
38173             callData = this.getCallData(data);\r
38174         }\r
38175 \r
38176         if(this.enableUrlEncode){\r
38177             var params = {};\r
38178             params[Ext.isString(this.enableUrlEncode) ? this.enableUrlEncode : 'data'] = Ext.encode(callData);\r
38179             o.params = params;\r
38180         }else{\r
38181             o.jsonData = callData;\r
38182         }\r
38183         Ext.Ajax.request(o);\r
38184     },\r
38185 \r
38186     combineAndSend : function(){\r
38187         var len = this.callBuffer.length;\r
38188         if(len > 0){\r
38189             this.doSend(len == 1 ? this.callBuffer[0] : this.callBuffer);\r
38190             this.callBuffer = [];\r
38191         }\r
38192     },\r
38193 \r
38194     queueTransaction: function(t){\r
38195         if(t.form){\r
38196             this.processForm(t);\r
38197             return;\r
38198         }\r
38199         this.callBuffer.push(t);\r
38200         if(this.enableBuffer){\r
38201             if(!this.callTask){\r
38202                 this.callTask = new Ext.util.DelayedTask(this.combineAndSend, this);\r
38203             }\r
38204             this.callTask.delay(Ext.isNumber(this.enableBuffer) ? this.enableBuffer : 10);\r
38205         }else{\r
38206             this.combineAndSend();\r
38207         }\r
38208     },\r
38209 \r
38210     doCall : function(c, m, args){\r
38211         var data = null, hs = args[m.len], scope = args[m.len+1];\r
38212 \r
38213         if(m.len !== 0){\r
38214             data = args.slice(0, m.len);\r
38215         }\r
38216 \r
38217         var t = new Ext.Direct.Transaction({\r
38218             provider: this,\r
38219             args: args,\r
38220             action: c,\r
38221             method: m.name,\r
38222             data: data,\r
38223             cb: scope && Ext.isFunction(hs) ? hs.createDelegate(scope) : hs\r
38224         });\r
38225 \r
38226         if(this.fireEvent('beforecall', this, t) !== false){\r
38227             Ext.Direct.addTransaction(t);\r
38228             this.queueTransaction(t);\r
38229             this.fireEvent('call', this, t);\r
38230         }\r
38231     },\r
38232 \r
38233     doForm : function(c, m, form, callback, scope){\r
38234         var t = new Ext.Direct.Transaction({\r
38235             provider: this,\r
38236             action: c,\r
38237             method: m.name,\r
38238             args:[form, callback, scope],\r
38239             cb: scope && Ext.isFunction(callback) ? callback.createDelegate(scope) : callback,\r
38240             isForm: true\r
38241         });\r
38242 \r
38243         if(this.fireEvent('beforecall', this, t) !== false){\r
38244             Ext.Direct.addTransaction(t);\r
38245             var isUpload = String(form.getAttribute("enctype")).toLowerCase() == 'multipart/form-data',\r
38246                 params = {\r
38247                     extTID: t.tid,\r
38248                     extAction: c,\r
38249                     extMethod: m.name,\r
38250                     extType: 'rpc',\r
38251                     extUpload: String(isUpload)\r
38252                 };\r
38253             \r
38254             // change made from typeof callback check to callback.params\r
38255             // to support addl param passing in DirectSubmit EAC 6/2\r
38256             Ext.apply(t, {\r
38257                 form: Ext.getDom(form),\r
38258                 isUpload: isUpload,\r
38259                 params: callback && Ext.isObject(callback.params) ? Ext.apply(params, callback.params) : params\r
38260             });\r
38261             this.fireEvent('call', this, t);\r
38262             this.processForm(t);\r
38263         }\r
38264     },\r
38265     \r
38266     processForm: function(t){\r
38267         Ext.Ajax.request({\r
38268             url: this.url,\r
38269             params: t.params,\r
38270             callback: this.onData,\r
38271             scope: this,\r
38272             form: t.form,\r
38273             isUpload: t.isUpload,\r
38274             ts: t\r
38275         });\r
38276     },\r
38277 \r
38278     createMethod : function(c, m){\r
38279         var f;\r
38280         if(!m.formHandler){\r
38281             f = function(){\r
38282                 this.doCall(c, m, Array.prototype.slice.call(arguments, 0));\r
38283             }.createDelegate(this);\r
38284         }else{\r
38285             f = function(form, callback, scope){\r
38286                 this.doForm(c, m, form, callback, scope);\r
38287             }.createDelegate(this);\r
38288         }\r
38289         f.directCfg = {\r
38290             action: c,\r
38291             method: m\r
38292         };\r
38293         return f;\r
38294     },\r
38295 \r
38296     getTransaction: function(opt){\r
38297         return opt && opt.tid ? Ext.Direct.getTransaction(opt.tid) : null;\r
38298     },\r
38299 \r
38300     doCallback: function(t, e){\r
38301         var fn = e.status ? 'success' : 'failure';\r
38302         if(t && t.cb){\r
38303             var hs = t.cb,\r
38304                 result = Ext.isDefined(e.result) ? e.result : e.data;\r
38305             if(Ext.isFunction(hs)){\r
38306                 hs(result, e);\r
38307             } else{\r
38308                 Ext.callback(hs[fn], hs.scope, [result, e]);\r
38309                 Ext.callback(hs.callback, hs.scope, [result, e]);\r
38310             }\r
38311         }\r
38312     }\r
38313 });\r
38314 Ext.Direct.PROVIDERS['remoting'] = Ext.direct.RemotingProvider;/*!\r
38315  * Ext JS Library 3.1.1\r
38316  * Copyright(c) 2006-2010 Ext JS, LLC\r
38317  * licensing@extjs.com\r
38318  * http://www.extjs.com/license\r
38319  */\r
38320 /**\r
38321  * @class Ext.Resizable\r
38322  * @extends Ext.util.Observable\r
38323  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element \r
38324  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap\r
38325  * the textarea in a div and set 'resizeChild' to true (or to the id of the element), <b>or</b> set wrap:true in your config and\r
38326  * the element will be wrapped for you automatically.</p>\r
38327  * <p>Here is the list of valid resize handles:</p>\r
38328  * <pre>\r
38329 Value   Description\r
38330 ------  -------------------\r
38331  'n'     north\r
38332  's'     south\r
38333  'e'     east\r
38334  'w'     west\r
38335  'nw'    northwest\r
38336  'sw'    southwest\r
38337  'se'    southeast\r
38338  'ne'    northeast\r
38339  'all'   all\r
38340 </pre>\r
38341  * <p>Here's an example showing the creation of a typical Resizable:</p>\r
38342  * <pre><code>\r
38343 var resizer = new Ext.Resizable('element-id', {\r
38344     handles: 'all',\r
38345     minWidth: 200,\r
38346     minHeight: 100,\r
38347     maxWidth: 500,\r
38348     maxHeight: 400,\r
38349     pinned: true\r
38350 });\r
38351 resizer.on('resize', myHandler);\r
38352 </code></pre>\r
38353  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>\r
38354  * resizer.east.setDisplayed(false);</p>\r
38355  * @constructor\r
38356  * Create a new resizable component\r
38357  * @param {Mixed} el The id or element to resize\r
38358  * @param {Object} config configuration options\r
38359   */\r
38360 Ext.Resizable = Ext.extend(Ext.util.Observable, {\r
38361     \r
38362     constructor: function(el, config){\r
38363         this.el = Ext.get(el);\r
38364         if(config && config.wrap){\r
38365             config.resizeChild = this.el;\r
38366             this.el = this.el.wrap(typeof config.wrap == 'object' ? config.wrap : {cls:'xresizable-wrap'});\r
38367             this.el.id = this.el.dom.id = config.resizeChild.id + '-rzwrap';\r
38368             this.el.setStyle('overflow', 'hidden');\r
38369             this.el.setPositioning(config.resizeChild.getPositioning());\r
38370             config.resizeChild.clearPositioning();\r
38371             if(!config.width || !config.height){\r
38372                 var csize = config.resizeChild.getSize();\r
38373                 this.el.setSize(csize.width, csize.height);\r
38374             }\r
38375             if(config.pinned && !config.adjustments){\r
38376                 config.adjustments = 'auto';\r
38377             }\r
38378         }\r
38379     \r
38380         /**\r
38381          * The proxy Element that is resized in place of the real Element during the resize operation.\r
38382          * This may be queried using {@link Ext.Element#getBox} to provide the new area to resize to.\r
38383          * Read only.\r
38384          * @type Ext.Element.\r
38385          * @property proxy\r
38386          */\r
38387         this.proxy = this.el.createProxy({tag: 'div', cls: 'x-resizable-proxy', id: this.el.id + '-rzproxy'}, Ext.getBody());\r
38388         this.proxy.unselectable();\r
38389         this.proxy.enableDisplayMode('block');\r
38390     \r
38391         Ext.apply(this, config);\r
38392         \r
38393         if(this.pinned){\r
38394             this.disableTrackOver = true;\r
38395             this.el.addClass('x-resizable-pinned');\r
38396         }\r
38397         // if the element isn't positioned, make it relative\r
38398         var position = this.el.getStyle('position');\r
38399         if(position != 'absolute' && position != 'fixed'){\r
38400             this.el.setStyle('position', 'relative');\r
38401         }\r
38402         if(!this.handles){ // no handles passed, must be legacy style\r
38403             this.handles = 's,e,se';\r
38404             if(this.multiDirectional){\r
38405                 this.handles += ',n,w';\r
38406             }\r
38407         }\r
38408         if(this.handles == 'all'){\r
38409             this.handles = 'n s e w ne nw se sw';\r
38410         }\r
38411         var hs = this.handles.split(/\s*?[,;]\s*?| /);\r
38412         var ps = Ext.Resizable.positions;\r
38413         for(var i = 0, len = hs.length; i < len; i++){\r
38414             if(hs[i] && ps[hs[i]]){\r
38415                 var pos = ps[hs[i]];\r
38416                 this[pos] = new Ext.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent, this.handleCls);\r
38417             }\r
38418         }\r
38419         // legacy\r
38420         this.corner = this.southeast;\r
38421         \r
38422         if(this.handles.indexOf('n') != -1 || this.handles.indexOf('w') != -1){\r
38423             this.updateBox = true;\r
38424         }   \r
38425        \r
38426         this.activeHandle = null;\r
38427         \r
38428         if(this.resizeChild){\r
38429             if(typeof this.resizeChild == 'boolean'){\r
38430                 this.resizeChild = Ext.get(this.el.dom.firstChild, true);\r
38431             }else{\r
38432                 this.resizeChild = Ext.get(this.resizeChild, true);\r
38433             }\r
38434         }\r
38435         \r
38436         if(this.adjustments == 'auto'){\r
38437             var rc = this.resizeChild;\r
38438             var hw = this.west, he = this.east, hn = this.north, hs = this.south;\r
38439             if(rc && (hw || hn)){\r
38440                 rc.position('relative');\r
38441                 rc.setLeft(hw ? hw.el.getWidth() : 0);\r
38442                 rc.setTop(hn ? hn.el.getHeight() : 0);\r
38443             }\r
38444             this.adjustments = [\r
38445                 (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),\r
38446                 (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1 \r
38447             ];\r
38448         }\r
38449         \r
38450         if(this.draggable){\r
38451             this.dd = this.dynamic ? \r
38452                 this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});\r
38453             this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);\r
38454             if(this.constrainTo){\r
38455                 this.dd.constrainTo(this.constrainTo);\r
38456             }\r
38457         }\r
38458         \r
38459         this.addEvents(\r
38460             /**\r
38461              * @event beforeresize\r
38462              * Fired before resize is allowed. Set {@link #enabled} to false to cancel resize.\r
38463              * @param {Ext.Resizable} this\r
38464              * @param {Ext.EventObject} e The mousedown event\r
38465              */\r
38466             'beforeresize',\r
38467             /**\r
38468              * @event resize\r
38469              * Fired after a resize.\r
38470              * @param {Ext.Resizable} this\r
38471              * @param {Number} width The new width\r
38472              * @param {Number} height The new height\r
38473              * @param {Ext.EventObject} e The mouseup event\r
38474              */\r
38475             'resize'\r
38476         );\r
38477         \r
38478         if(this.width !== null && this.height !== null){\r
38479             this.resizeTo(this.width, this.height);\r
38480         }else{\r
38481             this.updateChildSize();\r
38482         }\r
38483         if(Ext.isIE){\r
38484             this.el.dom.style.zoom = 1;\r
38485         }\r
38486         Ext.Resizable.superclass.constructor.call(this);    \r
38487     },\r
38488 \r
38489     /**\r
38490      * @cfg {Array/String} adjustments String 'auto' or an array [width, height] with values to be <b>added</b> to the\r
38491      * resize operation's new size (defaults to <tt>[0, 0]</tt>)\r
38492      */\r
38493     adjustments : [0, 0],\r
38494     /**\r
38495      * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)\r
38496      */\r
38497     animate : false,\r
38498     /**\r
38499      * @cfg {Mixed} constrainTo Constrain the resize to a particular element\r
38500      */\r
38501     /**\r
38502      * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)\r
38503      */\r
38504     disableTrackOver : false,\r
38505     /**\r
38506      * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)\r
38507      */\r
38508     draggable: false,\r
38509     /**\r
38510      * @cfg {Number} duration Animation duration if animate = true (defaults to 0.35)\r
38511      */\r
38512     duration : 0.35,\r
38513     /**\r
38514      * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)\r
38515      */\r
38516     dynamic : false,\r
38517     /**\r
38518      * @cfg {String} easing Animation easing if animate = true (defaults to <tt>'easingOutStrong'</tt>)\r
38519      */\r
38520     easing : 'easeOutStrong',\r
38521     /**\r
38522      * @cfg {Boolean} enabled False to disable resizing (defaults to true)\r
38523      */\r
38524     enabled : true,\r
38525     /**\r
38526      * @property enabled Writable. False if resizing is disabled.\r
38527      * @type Boolean \r
38528      */\r
38529     /**\r
38530      * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined).\r
38531      * Specify either <tt>'all'</tt> or any of <tt>'n s e w ne nw se sw'</tt>.\r
38532      */\r
38533     handles : false,\r
38534     /**\r
38535      * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  Deprecated style of adding multi-direction resize handles.\r
38536      */\r
38537     multiDirectional : false,\r
38538     /**\r
38539      * @cfg {Number} height The height of the element in pixels (defaults to null)\r
38540      */\r
38541     height : null,\r
38542     /**\r
38543      * @cfg {Number} width The width of the element in pixels (defaults to null)\r
38544      */\r
38545     width : null,\r
38546     /**\r
38547      * @cfg {Number} heightIncrement The increment to snap the height resize in pixels\r
38548      * (only applies if <code>{@link #dynamic}==true</code>). Defaults to <tt>0</tt>.\r
38549      */\r
38550     heightIncrement : 0,\r
38551     /**\r
38552      * @cfg {Number} widthIncrement The increment to snap the width resize in pixels\r
38553      * (only applies if <code>{@link #dynamic}==true</code>). Defaults to <tt>0</tt>.\r
38554      */\r
38555     widthIncrement : 0,\r
38556     /**\r
38557      * @cfg {Number} minHeight The minimum height for the element (defaults to 5)\r
38558      */\r
38559     minHeight : 5,\r
38560     /**\r
38561      * @cfg {Number} minWidth The minimum width for the element (defaults to 5)\r
38562      */\r
38563     minWidth : 5,\r
38564     /**\r
38565      * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)\r
38566      */\r
38567     maxHeight : 10000,\r
38568     /**\r
38569      * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)\r
38570      */\r
38571     maxWidth : 10000,\r
38572     /**\r
38573      * @cfg {Number} minX The minimum x for the element (defaults to 0)\r
38574      */\r
38575     minX: 0,\r
38576     /**\r
38577      * @cfg {Number} minY The minimum x for the element (defaults to 0)\r
38578      */\r
38579     minY: 0,\r
38580     /**\r
38581      * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the\r
38582      * user mouses over the resizable borders. This is only applied at config time. (defaults to false)\r
38583      */\r
38584     pinned : false,\r
38585     /**\r
38586      * @cfg {Boolean} preserveRatio True to preserve the original ratio between height\r
38587      * and width during resize (defaults to false)\r
38588      */\r
38589     preserveRatio : false,\r
38590     /**\r
38591      * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false) \r
38592      */ \r
38593     resizeChild : false,\r
38594     /**\r
38595      * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)\r
38596      */\r
38597     transparent: false,\r
38598     /**\r
38599      * @cfg {Ext.lib.Region} resizeRegion Constrain the resize to a particular region\r
38600      */\r
38601     /**\r
38602      * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)\r
38603      * in favor of the handles config option (defaults to false)\r
38604      */\r
38605     /**\r
38606      * @cfg {String} handleCls A css class to add to each handle. Defaults to <tt>''</tt>.\r
38607      */\r
38608 \r
38609     \r
38610     /**\r
38611      * Perform a manual resize and fires the 'resize' event.\r
38612      * @param {Number} width\r
38613      * @param {Number} height\r
38614      */\r
38615     resizeTo : function(width, height){\r
38616         this.el.setSize(width, height);\r
38617         this.updateChildSize();\r
38618         this.fireEvent('resize', this, width, height, null);\r
38619     },\r
38620 \r
38621     // private\r
38622     startSizing : function(e, handle){\r
38623         this.fireEvent('beforeresize', this, e);\r
38624         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler\r
38625 \r
38626             if(!this.overlay){\r
38627                 this.overlay = this.el.createProxy({tag: 'div', cls: 'x-resizable-overlay', html: '&#160;'}, Ext.getBody());\r
38628                 this.overlay.unselectable();\r
38629                 this.overlay.enableDisplayMode('block');\r
38630                 this.overlay.on({\r
38631                     scope: this,\r
38632                     mousemove: this.onMouseMove,\r
38633                     mouseup: this.onMouseUp\r
38634                 });\r
38635             }\r
38636             this.overlay.setStyle('cursor', handle.el.getStyle('cursor'));\r
38637 \r
38638             this.resizing = true;\r
38639             this.startBox = this.el.getBox();\r
38640             this.startPoint = e.getXY();\r
38641             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],\r
38642                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];\r
38643 \r
38644             this.overlay.setSize(Ext.lib.Dom.getViewWidth(true), Ext.lib.Dom.getViewHeight(true));\r
38645             this.overlay.show();\r
38646 \r
38647             if(this.constrainTo) {\r
38648                 var ct = Ext.get(this.constrainTo);\r
38649                 this.resizeRegion = ct.getRegion().adjust(\r
38650                     ct.getFrameWidth('t'),\r
38651                     ct.getFrameWidth('l'),\r
38652                     -ct.getFrameWidth('b'),\r
38653                     -ct.getFrameWidth('r')\r
38654                 );\r
38655             }\r
38656 \r
38657             this.proxy.setStyle('visibility', 'hidden'); // workaround display none\r
38658             this.proxy.show();\r
38659             this.proxy.setBox(this.startBox);\r
38660             if(!this.dynamic){\r
38661                 this.proxy.setStyle('visibility', 'visible');\r
38662             }\r
38663         }\r
38664     },\r
38665 \r
38666     // private\r
38667     onMouseDown : function(handle, e){\r
38668         if(this.enabled){\r
38669             e.stopEvent();\r
38670             this.activeHandle = handle;\r
38671             this.startSizing(e, handle);\r
38672         }          \r
38673     },\r
38674 \r
38675     // private\r
38676     onMouseUp : function(e){\r
38677         this.activeHandle = null;\r
38678         var size = this.resizeElement();\r
38679         this.resizing = false;\r
38680         this.handleOut();\r
38681         this.overlay.hide();\r
38682         this.proxy.hide();\r
38683         this.fireEvent('resize', this, size.width, size.height, e);\r
38684     },\r
38685 \r
38686     // private\r
38687     updateChildSize : function(){\r
38688         if(this.resizeChild){\r
38689             var el = this.el;\r
38690             var child = this.resizeChild;\r
38691             var adj = this.adjustments;\r
38692             if(el.dom.offsetWidth){\r
38693                 var b = el.getSize(true);\r
38694                 child.setSize(b.width+adj[0], b.height+adj[1]);\r
38695             }\r
38696             // Second call here for IE\r
38697             // The first call enables instant resizing and\r
38698             // the second call corrects scroll bars if they\r
38699             // exist\r
38700             if(Ext.isIE){\r
38701                 setTimeout(function(){\r
38702                     if(el.dom.offsetWidth){\r
38703                         var b = el.getSize(true);\r
38704                         child.setSize(b.width+adj[0], b.height+adj[1]);\r
38705                     }\r
38706                 }, 10);\r
38707             }\r
38708         }\r
38709     },\r
38710 \r
38711     // private\r
38712     snap : function(value, inc, min){\r
38713         if(!inc || !value){\r
38714             return value;\r
38715         }\r
38716         var newValue = value;\r
38717         var m = value % inc;\r
38718         if(m > 0){\r
38719             if(m > (inc/2)){\r
38720                 newValue = value + (inc-m);\r
38721             }else{\r
38722                 newValue = value - m;\r
38723             }\r
38724         }\r
38725         return Math.max(min, newValue);\r
38726     },\r
38727 \r
38728     /**\r
38729      * <p>Performs resizing of the associated Element. This method is called internally by this\r
38730      * class, and should not be called by user code.</p>\r
38731      * <p>If a Resizable is being used to resize an Element which encapsulates a more complex UI\r
38732      * component such as a Panel, this method may be overridden by specifying an implementation\r
38733      * as a config option to provide appropriate behaviour at the end of the resize operation on\r
38734      * mouseup, for example resizing the Panel, and relaying the Panel's content.</p>\r
38735      * <p>The new area to be resized to is available by examining the state of the {@link #proxy}\r
38736      * Element. Example:\r
38737 <pre><code>\r
38738 new Ext.Panel({\r
38739     title: 'Resize me',\r
38740     x: 100,\r
38741     y: 100,\r
38742     renderTo: Ext.getBody(),\r
38743     floating: true,\r
38744     frame: true,\r
38745     width: 400,\r
38746     height: 200,\r
38747     listeners: {\r
38748         render: function(p) {\r
38749             new Ext.Resizable(p.getEl(), {\r
38750                 handles: 'all',\r
38751                 pinned: true,\r
38752                 transparent: true,\r
38753                 resizeElement: function() {\r
38754                     var box = this.proxy.getBox();\r
38755                     p.updateBox(box);\r
38756                     if (p.layout) {\r
38757                         p.doLayout();\r
38758                     }\r
38759                     return box;\r
38760                 }\r
38761            });\r
38762        }\r
38763     }\r
38764 }).show();\r
38765 </code></pre>\r
38766      */\r
38767     resizeElement : function(){\r
38768         var box = this.proxy.getBox();\r
38769         if(this.updateBox){\r
38770             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);\r
38771         }else{\r
38772             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);\r
38773         }\r
38774         this.updateChildSize();\r
38775         if(!this.dynamic){\r
38776             this.proxy.hide();\r
38777         }\r
38778         if(this.draggable && this.constrainTo){\r
38779             this.dd.resetConstraints();\r
38780             this.dd.constrainTo(this.constrainTo);\r
38781         }\r
38782         return box;\r
38783     },\r
38784 \r
38785     // private\r
38786     constrain : function(v, diff, m, mx){\r
38787         if(v - diff < m){\r
38788             diff = v - m;    \r
38789         }else if(v - diff > mx){\r
38790             diff = v - mx; \r
38791         }\r
38792         return diff;                \r
38793     },\r
38794 \r
38795     // private\r
38796     onMouseMove : function(e){\r
38797         if(this.enabled && this.activeHandle){\r
38798             try{// try catch so if something goes wrong the user doesn't get hung\r
38799 \r
38800             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {\r
38801                 return;\r
38802             }\r
38803 \r
38804             //var curXY = this.startPoint;\r
38805             var curSize = this.curSize || this.startBox,\r
38806                 x = this.startBox.x, y = this.startBox.y,\r
38807                 ox = x, \r
38808                 oy = y,\r
38809                 w = curSize.width, \r
38810                 h = curSize.height,\r
38811                 ow = w, \r
38812                 oh = h,\r
38813                 mw = this.minWidth, \r
38814                 mh = this.minHeight,\r
38815                 mxw = this.maxWidth, \r
38816                 mxh = this.maxHeight,\r
38817                 wi = this.widthIncrement,\r
38818                 hi = this.heightIncrement,\r
38819                 eventXY = e.getXY(),\r
38820                 diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0])),\r
38821                 diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1])),\r
38822                 pos = this.activeHandle.position,\r
38823                 tw,\r
38824                 th;\r
38825             \r
38826             switch(pos){\r
38827                 case 'east':\r
38828                     w += diffX; \r
38829                     w = Math.min(Math.max(mw, w), mxw);\r
38830                     break;\r
38831                 case 'south':\r
38832                     h += diffY;\r
38833                     h = Math.min(Math.max(mh, h), mxh);\r
38834                     break;\r
38835                 case 'southeast':\r
38836                     w += diffX; \r
38837                     h += diffY;\r
38838                     w = Math.min(Math.max(mw, w), mxw);\r
38839                     h = Math.min(Math.max(mh, h), mxh);\r
38840                     break;\r
38841                 case 'north':\r
38842                     diffY = this.constrain(h, diffY, mh, mxh);\r
38843                     y += diffY;\r
38844                     h -= diffY;\r
38845                     break;\r
38846                 case 'west':\r
38847                     diffX = this.constrain(w, diffX, mw, mxw);\r
38848                     x += diffX;\r
38849                     w -= diffX;\r
38850                     break;\r
38851                 case 'northeast':\r
38852                     w += diffX; \r
38853                     w = Math.min(Math.max(mw, w), mxw);\r
38854                     diffY = this.constrain(h, diffY, mh, mxh);\r
38855                     y += diffY;\r
38856                     h -= diffY;\r
38857                     break;\r
38858                 case 'northwest':\r
38859                     diffX = this.constrain(w, diffX, mw, mxw);\r
38860                     diffY = this.constrain(h, diffY, mh, mxh);\r
38861                     y += diffY;\r
38862                     h -= diffY;\r
38863                     x += diffX;\r
38864                     w -= diffX;\r
38865                     break;\r
38866                case 'southwest':\r
38867                     diffX = this.constrain(w, diffX, mw, mxw);\r
38868                     h += diffY;\r
38869                     h = Math.min(Math.max(mh, h), mxh);\r
38870                     x += diffX;\r
38871                     w -= diffX;\r
38872                     break;\r
38873             }\r
38874             \r
38875             var sw = this.snap(w, wi, mw);\r
38876             var sh = this.snap(h, hi, mh);\r
38877             if(sw != w || sh != h){\r
38878                 switch(pos){\r
38879                     case 'northeast':\r
38880                         y -= sh - h;\r
38881                     break;\r
38882                     case 'north':\r
38883                         y -= sh - h;\r
38884                         break;\r
38885                     case 'southwest':\r
38886                         x -= sw - w;\r
38887                     break;\r
38888                     case 'west':\r
38889                         x -= sw - w;\r
38890                         break;\r
38891                     case 'northwest':\r
38892                         x -= sw - w;\r
38893                         y -= sh - h;\r
38894                     break;\r
38895                 }\r
38896                 w = sw;\r
38897                 h = sh;\r
38898             }\r
38899             \r
38900             if(this.preserveRatio){\r
38901                 switch(pos){\r
38902                     case 'southeast':\r
38903                     case 'east':\r
38904                         h = oh * (w/ow);\r
38905                         h = Math.min(Math.max(mh, h), mxh);\r
38906                         w = ow * (h/oh);\r
38907                        break;\r
38908                     case 'south':\r
38909                         w = ow * (h/oh);\r
38910                         w = Math.min(Math.max(mw, w), mxw);\r
38911                         h = oh * (w/ow);\r
38912                         break;\r
38913                     case 'northeast':\r
38914                         w = ow * (h/oh);\r
38915                         w = Math.min(Math.max(mw, w), mxw);\r
38916                         h = oh * (w/ow);\r
38917                     break;\r
38918                     case 'north':\r
38919                         tw = w;\r
38920                         w = ow * (h/oh);\r
38921                         w = Math.min(Math.max(mw, w), mxw);\r
38922                         h = oh * (w/ow);\r
38923                         x += (tw - w) / 2;\r
38924                         break;\r
38925                     case 'southwest':\r
38926                         h = oh * (w/ow);\r
38927                         h = Math.min(Math.max(mh, h), mxh);\r
38928                         tw = w;\r
38929                         w = ow * (h/oh);\r
38930                         x += tw - w;\r
38931                         break;\r
38932                     case 'west':\r
38933                         th = h;\r
38934                         h = oh * (w/ow);\r
38935                         h = Math.min(Math.max(mh, h), mxh);\r
38936                         y += (th - h) / 2;\r
38937                         tw = w;\r
38938                         w = ow * (h/oh);\r
38939                         x += tw - w;\r
38940                        break;\r
38941                     case 'northwest':\r
38942                         tw = w;\r
38943                         th = h;\r
38944                         h = oh * (w/ow);\r
38945                         h = Math.min(Math.max(mh, h), mxh);\r
38946                         w = ow * (h/oh);\r
38947                         y += th - h;\r
38948                         x += tw - w;\r
38949                         break;\r
38950                         \r
38951                 }\r
38952             }\r
38953             this.proxy.setBounds(x, y, w, h);\r
38954             if(this.dynamic){\r
38955                 this.resizeElement();\r
38956             }\r
38957             }catch(ex){}\r
38958         }\r
38959     },\r
38960 \r
38961     // private\r
38962     handleOver : function(){\r
38963         if(this.enabled){\r
38964             this.el.addClass('x-resizable-over');\r
38965         }\r
38966     },\r
38967 \r
38968     // private\r
38969     handleOut : function(){\r
38970         if(!this.resizing){\r
38971             this.el.removeClass('x-resizable-over');\r
38972         }\r
38973     },\r
38974     \r
38975     /**\r
38976      * Returns the element this component is bound to.\r
38977      * @return {Ext.Element}\r
38978      */\r
38979     getEl : function(){\r
38980         return this.el;\r
38981     },\r
38982     \r
38983     /**\r
38984      * Returns the resizeChild element (or null).\r
38985      * @return {Ext.Element}\r
38986      */\r
38987     getResizeChild : function(){\r
38988         return this.resizeChild;\r
38989     },\r
38990     \r
38991     /**\r
38992      * Destroys this resizable. If the element was wrapped and \r
38993      * removeEl is not true then the element remains.\r
38994      * @param {Boolean} removeEl (optional) true to remove the element from the DOM\r
38995      */\r
38996     destroy : function(removeEl){\r
38997         Ext.destroy(this.dd, this.overlay, this.proxy);\r
38998         this.overlay = null;\r
38999         this.proxy = null;\r
39000         \r
39001         var ps = Ext.Resizable.positions;\r
39002         for(var k in ps){\r
39003             if(typeof ps[k] != 'function' && this[ps[k]]){\r
39004                 this[ps[k]].destroy();\r
39005             }\r
39006         }\r
39007         if(removeEl){\r
39008             this.el.update('');\r
39009             Ext.destroy(this.el);\r
39010             this.el = null;\r
39011         }\r
39012         this.purgeListeners();\r
39013     },\r
39014 \r
39015     syncHandleHeight : function(){\r
39016         var h = this.el.getHeight(true);\r
39017         if(this.west){\r
39018             this.west.el.setHeight(h);\r
39019         }\r
39020         if(this.east){\r
39021             this.east.el.setHeight(h);\r
39022         }\r
39023     }\r
39024 });\r
39025 \r
39026 // private\r
39027 // hash to map config positions to true positions\r
39028 Ext.Resizable.positions = {\r
39029     n: 'north', s: 'south', e: 'east', w: 'west', se: 'southeast', sw: 'southwest', nw: 'northwest', ne: 'northeast'\r
39030 };\r
39031 \r
39032 Ext.Resizable.Handle = Ext.extend(Object, {\r
39033     constructor : function(rz, pos, disableTrackOver, transparent, cls){\r
39034        if(!this.tpl){\r
39035             // only initialize the template if resizable is used\r
39036             var tpl = Ext.DomHelper.createTemplate(\r
39037                 {tag: 'div', cls: 'x-resizable-handle x-resizable-handle-{0}'}\r
39038             );\r
39039             tpl.compile();\r
39040             Ext.Resizable.Handle.prototype.tpl = tpl;\r
39041         }\r
39042         this.position = pos;\r
39043         this.rz = rz;\r
39044         this.el = this.tpl.append(rz.el.dom, [this.position], true);\r
39045         this.el.unselectable();\r
39046         if(transparent){\r
39047             this.el.setOpacity(0);\r
39048         }\r
39049         if(!Ext.isEmpty(cls)){\r
39050             this.el.addClass(cls);    \r
39051         }\r
39052         this.el.on('mousedown', this.onMouseDown, this);\r
39053         if(!disableTrackOver){\r
39054             this.el.on({\r
39055                 scope: this,\r
39056                 mouseover: this.onMouseOver,\r
39057                 mouseout: this.onMouseOut\r
39058             });\r
39059         }        \r
39060     },\r
39061     \r
39062     // private\r
39063     afterResize : function(rz){\r
39064         // do nothing    \r
39065     },\r
39066     // private\r
39067     onMouseDown : function(e){\r
39068         this.rz.onMouseDown(this, e);\r
39069     },\r
39070     // private\r
39071     onMouseOver : function(e){\r
39072         this.rz.handleOver(this, e);\r
39073     },\r
39074     // private\r
39075     onMouseOut : function(e){\r
39076         this.rz.handleOut(this, e);\r
39077     },\r
39078     // private\r
39079     destroy : function(){\r
39080         Ext.destroy(this.el);\r
39081         this.el = null;\r
39082     }\r
39083 });\r
39084 /**
39085  * @class Ext.Window
39086  * @extends Ext.Panel
39087  * <p>A specialized panel intended for use as an application window.  Windows are floated, {@link #resizable}, and
39088  * {@link #draggable} by default.  Windows can be {@link #maximizable maximized} to fill the viewport,
39089  * restored to their prior size, and can be {@link #minimize}d.</p>
39090  * <p>Windows can also be linked to a {@link Ext.WindowGroup} or managed by the {@link Ext.WindowMgr} to provide
39091  * grouping, activation, to front, to back and other application-specific behavior.</p>
39092  * <p>By default, Windows will be rendered to document.body. To {@link #constrain} a Window to another element
39093  * specify {@link Ext.Component#renderTo renderTo}.</p>
39094  * <p><b>Note:</b> By default, the <code>{@link #closable close}</code> header tool <i>destroys</i> the Window resulting in
39095  * destruction of any child Components. This makes the Window object, and all its descendants <b>unusable</b>. To enable
39096  * re-use of a Window, use <b><code>{@link #closeAction closeAction: 'hide'}</code></b>.</p>
39097  * @constructor
39098  * @param {Object} config The config object
39099  * @xtype window
39100  */
39101 Ext.Window = Ext.extend(Ext.Panel, {
39102     /**
39103      * @cfg {Number} x
39104      * The X position of the left edge of the window on initial showing. Defaults to centering the Window within
39105      * the width of the Window's container {@link Ext.Element Element) (The Element that the Window is rendered to).
39106      */
39107     /**
39108      * @cfg {Number} y
39109      * The Y position of the top edge of the window on initial showing. Defaults to centering the Window within
39110      * the height of the Window's container {@link Ext.Element Element) (The Element that the Window is rendered to).
39111      */
39112     /**
39113      * @cfg {Boolean} modal
39114      * True to make the window modal and mask everything behind it when displayed, false to display it without
39115      * restricting access to other UI elements (defaults to false).
39116      */
39117     /**
39118      * @cfg {String/Element} animateTarget
39119      * Id or element from which the window should animate while opening (defaults to null with no animation).
39120      */
39121     /**
39122      * @cfg {String} resizeHandles
39123      * A valid {@link Ext.Resizable} handles config string (defaults to 'all').  Only applies when resizable = true.
39124      */
39125     /**
39126      * @cfg {Ext.WindowGroup} manager
39127      * A reference to the WindowGroup that should manage this window (defaults to {@link Ext.WindowMgr}).
39128      */
39129     /**
39130     * @cfg {String/Number/Component} defaultButton
39131     * <p>Specifies a Component to receive focus when this Window is focussed.</p>
39132     * <p>This may be one of:</p><div class="mdetail-params"><ul>
39133     * <li>The index of a footer Button.</li>
39134     * <li>The id of a Component.</li>
39135     * <li>A Component.</li>
39136     * </ul></div>
39137     */
39138     /**
39139     * @cfg {Function} onEsc
39140     * Allows override of the built-in processing for the escape key. Default action
39141     * is to close the Window (performing whatever action is specified in {@link #closeAction}.
39142     * To prevent the Window closing when the escape key is pressed, specify this as
39143     * Ext.emptyFn (See {@link Ext#emptyFn}).
39144     */
39145     /**
39146      * @cfg {Boolean} collapsed
39147      * True to render the window collapsed, false to render it expanded (defaults to false). Note that if
39148      * {@link #expandOnShow} is true (the default) it will override the <tt>collapsed</tt> config and the window
39149      * will always be expanded when shown.
39150      */
39151     /**
39152      * @cfg {Boolean} maximized
39153      * True to initially display the window in a maximized state. (Defaults to false).
39154      */
39155
39156     /**
39157     * @cfg {String} baseCls
39158     * The base CSS class to apply to this panel's element (defaults to 'x-window').
39159     */
39160     baseCls : 'x-window',
39161     /**
39162      * @cfg {Boolean} resizable
39163      * True to allow user resizing at each edge and corner of the window, false to disable resizing (defaults to true).
39164      */
39165     resizable : true,
39166     /**
39167      * @cfg {Boolean} draggable
39168      * True to allow the window to be dragged by the header bar, false to disable dragging (defaults to true).  Note
39169      * that by default the window will be centered in the viewport, so if dragging is disabled the window may need
39170      * to be positioned programmatically after render (e.g., myWindow.setPosition(100, 100);).
39171      */
39172     draggable : true,
39173     /**
39174      * @cfg {Boolean} closable
39175      * <p>True to display the 'close' tool button and allow the user to close the window, false to
39176      * hide the button and disallow closing the window (defaults to true).</p>
39177      * <p>By default, when close is requested by either clicking the close button in the header
39178      * or pressing ESC when the Window has focus, the {@link #close} method will be called. This
39179      * will <i>{@link Ext.Component#destroy destroy}</i> the Window and its content meaning that
39180      * it may not be reused.</p>
39181      * <p>To make closing a Window <i>hide</i> the Window so that it may be reused, set
39182      * {@link #closeAction} to 'hide'.
39183      */
39184     closable : true,
39185     /**
39186      * @cfg {String} closeAction
39187      * <p>The action to take when the close header tool is clicked:
39188      * <div class="mdetail-params"><ul>
39189      * <li><b><code>'{@link #close}'</code></b> : <b>Default</b><div class="sub-desc">
39190      * {@link #close remove} the window from the DOM and {@link Ext.Component#destroy destroy}
39191      * it and all descendant Components. The window will <b>not</b> be available to be
39192      * redisplayed via the {@link #show} method.
39193      * </div></li>
39194      * <li><b><code>'{@link #hide}'</code></b> : <div class="sub-desc">
39195      * {@link #hide} the window by setting visibility to hidden and applying negative offsets.
39196      * The window will be available to be redisplayed via the {@link #show} method.
39197      * </div></li>
39198      * </ul></div>
39199      * <p><b>Note:</b> This setting does not affect the {@link #close} method
39200      * which will always {@link Ext.Component#destroy destroy} the window. To
39201      * programatically <i>hide</i> a window, call {@link #hide}.</p>
39202      */
39203     closeAction : 'close',
39204     /**
39205      * @cfg {Boolean} constrain
39206      * True to constrain the window within its containing element, false to allow it to fall outside of its
39207      * containing element. By default the window will be rendered to document.body.  To render and constrain the
39208      * window within another element specify {@link #renderTo}.
39209      * (defaults to false).  Optionally the header only can be constrained using {@link #constrainHeader}.
39210      */
39211     constrain : false,
39212     /**
39213      * @cfg {Boolean} constrainHeader
39214      * True to constrain the window header within its containing element (allowing the window body to fall outside
39215      * of its containing element) or false to allow the header to fall outside its containing element (defaults to
39216      * false). Optionally the entire window can be constrained using {@link #constrain}.
39217      */
39218     constrainHeader : false,
39219     /**
39220      * @cfg {Boolean} plain
39221      * True to render the window body with a transparent background so that it will blend into the framing
39222      * elements, false to add a lighter background color to visually highlight the body element and separate it
39223      * more distinctly from the surrounding frame (defaults to false).
39224      */
39225     plain : false,
39226     /**
39227      * @cfg {Boolean} minimizable
39228      * True to display the 'minimize' tool button and allow the user to minimize the window, false to hide the button
39229      * and disallow minimizing the window (defaults to false).  Note that this button provides no implementation --
39230      * the behavior of minimizing a window is implementation-specific, so the minimize event must be handled and a
39231      * custom minimize behavior implemented for this option to be useful.
39232      */
39233     minimizable : false,
39234     /**
39235      * @cfg {Boolean} maximizable
39236      * True to display the 'maximize' tool button and allow the user to maximize the window, false to hide the button
39237      * and disallow maximizing the window (defaults to false).  Note that when a window is maximized, the tool button
39238      * will automatically change to a 'restore' button with the appropriate behavior already built-in that will
39239      * restore the window to its previous size.
39240      */
39241     maximizable : false,
39242     /**
39243      * @cfg {Number} minHeight
39244      * The minimum height in pixels allowed for this window (defaults to 100).  Only applies when resizable = true.
39245      */
39246     minHeight : 100,
39247     /**
39248      * @cfg {Number} minWidth
39249      * The minimum width in pixels allowed for this window (defaults to 200).  Only applies when resizable = true.
39250      */
39251     minWidth : 200,
39252     /**
39253      * @cfg {Boolean} expandOnShow
39254      * True to always expand the window when it is displayed, false to keep it in its current state (which may be
39255      * {@link #collapsed}) when displayed (defaults to true).
39256      */
39257     expandOnShow : true,
39258
39259     // inherited docs, same default
39260     collapsible : false,
39261
39262     /**
39263      * @cfg {Boolean} initHidden
39264      * True to hide the window until show() is explicitly called (defaults to true).
39265      * @deprecated
39266      */
39267     initHidden : undefined,
39268
39269     /**
39270      * @cfg {Boolean} hidden
39271      * Render this component hidden (default is <tt>true</tt>). If <tt>true</tt>, the
39272      * {@link #hide} method will be called internally.
39273      */
39274     hidden : true,
39275
39276     // The following configs are set to provide the basic functionality of a window.
39277     // Changing them would require additional code to handle correctly and should
39278     // usually only be done in subclasses that can provide custom behavior.  Changing them
39279     // may have unexpected or undesirable results.
39280     /** @cfg {String} elements @hide */
39281     elements : 'header,body',
39282     /** @cfg {Boolean} frame @hide */
39283     frame : true,
39284     /** @cfg {Boolean} floating @hide */
39285     floating : true,
39286
39287     // private
39288     initComponent : function(){
39289         this.initTools();
39290         Ext.Window.superclass.initComponent.call(this);
39291         this.addEvents(
39292             /**
39293              * @event activate
39294              * Fires after the window has been visually activated via {@link #setActive}.
39295              * @param {Ext.Window} this
39296              */
39297             /**
39298              * @event deactivate
39299              * Fires after the window has been visually deactivated via {@link #setActive}.
39300              * @param {Ext.Window} this
39301              */
39302             /**
39303              * @event resize
39304              * Fires after the window has been resized.
39305              * @param {Ext.Window} this
39306              * @param {Number} width The window's new width
39307              * @param {Number} height The window's new height
39308              */
39309             'resize',
39310             /**
39311              * @event maximize
39312              * Fires after the window has been maximized.
39313              * @param {Ext.Window} this
39314              */
39315             'maximize',
39316             /**
39317              * @event minimize
39318              * Fires after the window has been minimized.
39319              * @param {Ext.Window} this
39320              */
39321             'minimize',
39322             /**
39323              * @event restore
39324              * Fires after the window has been restored to its original size after being maximized.
39325              * @param {Ext.Window} this
39326              */
39327             'restore'
39328         );
39329         // for backwards compat, this should be removed at some point
39330         if(Ext.isDefined(this.initHidden)){
39331             this.hidden = this.initHidden;
39332         }
39333         if(this.hidden === false){
39334             this.hidden = true;
39335             this.show();
39336         }
39337     },
39338
39339     // private
39340     getState : function(){
39341         return Ext.apply(Ext.Window.superclass.getState.call(this) || {}, this.getBox(true));
39342     },
39343
39344     // private
39345     onRender : function(ct, position){
39346         Ext.Window.superclass.onRender.call(this, ct, position);
39347
39348         if(this.plain){
39349             this.el.addClass('x-window-plain');
39350         }
39351
39352         // this element allows the Window to be focused for keyboard events
39353         this.focusEl = this.el.createChild({
39354                     tag: 'a', href:'#', cls:'x-dlg-focus',
39355                     tabIndex:'-1', html: '&#160;'});
39356         this.focusEl.swallowEvent('click', true);
39357
39358         this.proxy = this.el.createProxy('x-window-proxy');
39359         this.proxy.enableDisplayMode('block');
39360
39361         if(this.modal){
39362             this.mask = this.container.createChild({cls:'ext-el-mask'}, this.el.dom);
39363             this.mask.enableDisplayMode('block');
39364             this.mask.hide();
39365             this.mon(this.mask, 'click', this.focus, this);
39366         }
39367         if(this.maximizable){
39368             this.mon(this.header, 'dblclick', this.toggleMaximize, this);
39369         }
39370     },
39371
39372     // private
39373     initEvents : function(){
39374         Ext.Window.superclass.initEvents.call(this);
39375         if(this.animateTarget){
39376             this.setAnimateTarget(this.animateTarget);
39377         }
39378
39379         if(this.resizable){
39380             this.resizer = new Ext.Resizable(this.el, {
39381                 minWidth: this.minWidth,
39382                 minHeight:this.minHeight,
39383                 handles: this.resizeHandles || 'all',
39384                 pinned: true,
39385                 resizeElement : this.resizerAction,
39386                 handleCls: 'x-window-handle'
39387             });
39388             this.resizer.window = this;
39389             this.mon(this.resizer, 'beforeresize', this.beforeResize, this);
39390         }
39391
39392         if(this.draggable){
39393             this.header.addClass('x-window-draggable');
39394         }
39395         this.mon(this.el, 'mousedown', this.toFront, this);
39396         this.manager = this.manager || Ext.WindowMgr;
39397         this.manager.register(this);
39398         if(this.maximized){
39399             this.maximized = false;
39400             this.maximize();
39401         }
39402         if(this.closable){
39403             var km = this.getKeyMap();
39404             km.on(27, this.onEsc, this);
39405             km.disable();
39406         }
39407     },
39408
39409     initDraggable : function(){
39410         /**
39411          * If this Window is configured {@link #draggable}, this property will contain
39412          * an instance of {@link Ext.dd.DD} which handles dragging the Window's DOM Element.
39413          * @type Ext.dd.DD
39414          * @property dd
39415          */
39416         this.dd = new Ext.Window.DD(this);
39417     },
39418
39419    // private
39420     onEsc : function(k, e){
39421         e.stopEvent();
39422         this[this.closeAction]();
39423     },
39424
39425     // private
39426     beforeDestroy : function(){
39427         if (this.rendered){
39428             this.hide();
39429           if(this.doAnchor){
39430                 Ext.EventManager.removeResizeListener(this.doAnchor, this);
39431               Ext.EventManager.un(window, 'scroll', this.doAnchor, this);
39432             }
39433             Ext.destroy(
39434                 this.focusEl,
39435                 this.resizer,
39436                 this.dd,
39437                 this.proxy,
39438                 this.mask
39439             );
39440         }
39441         Ext.Window.superclass.beforeDestroy.call(this);
39442     },
39443
39444     // private
39445     onDestroy : function(){
39446         if(this.manager){
39447             this.manager.unregister(this);
39448         }
39449         Ext.Window.superclass.onDestroy.call(this);
39450     },
39451
39452     // private
39453     initTools : function(){
39454         if(this.minimizable){
39455             this.addTool({
39456                 id: 'minimize',
39457                 handler: this.minimize.createDelegate(this, [])
39458             });
39459         }
39460         if(this.maximizable){
39461             this.addTool({
39462                 id: 'maximize',
39463                 handler: this.maximize.createDelegate(this, [])
39464             });
39465             this.addTool({
39466                 id: 'restore',
39467                 handler: this.restore.createDelegate(this, []),
39468                 hidden:true
39469             });
39470         }
39471         if(this.closable){
39472             this.addTool({
39473                 id: 'close',
39474                 handler: this[this.closeAction].createDelegate(this, [])
39475             });
39476         }
39477     },
39478
39479     // private
39480     resizerAction : function(){
39481         var box = this.proxy.getBox();
39482         this.proxy.hide();
39483         this.window.handleResize(box);
39484         return box;
39485     },
39486
39487     // private
39488     beforeResize : function(){
39489         this.resizer.minHeight = Math.max(this.minHeight, this.getFrameHeight() + 40); // 40 is a magic minimum content size?
39490         this.resizer.minWidth = Math.max(this.minWidth, this.getFrameWidth() + 40);
39491         this.resizeBox = this.el.getBox();
39492     },
39493
39494     // private
39495     updateHandles : function(){
39496         if(Ext.isIE && this.resizer){
39497             this.resizer.syncHandleHeight();
39498             this.el.repaint();
39499         }
39500     },
39501
39502     // private
39503     handleResize : function(box){
39504         var rz = this.resizeBox;
39505         if(rz.x != box.x || rz.y != box.y){
39506             this.updateBox(box);
39507         }else{
39508             this.setSize(box);
39509         }
39510         this.focus();
39511         this.updateHandles();
39512         this.saveState();
39513         this.doLayout();
39514     },
39515
39516     /**
39517      * Focuses the window.  If a defaultButton is set, it will receive focus, otherwise the
39518      * window itself will receive focus.
39519      */
39520     focus : function(){
39521         var f = this.focusEl, db = this.defaultButton, t = typeof db;
39522         if(Ext.isDefined(db)){
39523             if(Ext.isNumber(db) && this.fbar){
39524                 f = this.fbar.items.get(db);
39525             }else if(Ext.isString(db)){
39526                 f = Ext.getCmp(db);
39527             }else{
39528                 f = db;
39529             }
39530         }
39531         f = f || this.focusEl;
39532         f.focus.defer(10, f);
39533     },
39534
39535     /**
39536      * Sets the target element from which the window should animate while opening.
39537      * @param {String/Element} el The target element or id
39538      */
39539     setAnimateTarget : function(el){
39540         el = Ext.get(el);
39541         this.animateTarget = el;
39542     },
39543
39544     // private
39545     beforeShow : function(){
39546         delete this.el.lastXY;
39547         delete this.el.lastLT;
39548         if(this.x === undefined || this.y === undefined){
39549             var xy = this.el.getAlignToXY(this.container, 'c-c');
39550             var pos = this.el.translatePoints(xy[0], xy[1]);
39551             this.x = this.x === undefined? pos.left : this.x;
39552             this.y = this.y === undefined? pos.top : this.y;
39553         }
39554         this.el.setLeftTop(this.x, this.y);
39555
39556         if(this.expandOnShow){
39557             this.expand(false);
39558         }
39559
39560         if(this.modal){
39561             Ext.getBody().addClass('x-body-masked');
39562             this.mask.setSize(Ext.lib.Dom.getViewWidth(true), Ext.lib.Dom.getViewHeight(true));
39563             this.mask.show();
39564         }
39565     },
39566
39567     /**
39568      * Shows the window, rendering it first if necessary, or activates it and brings it to front if hidden.
39569      * @param {String/Element} animateTarget (optional) The target element or id from which the window should
39570      * animate while opening (defaults to null with no animation)
39571      * @param {Function} callback (optional) A callback function to call after the window is displayed
39572      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the callback is executed. Defaults to this Window.
39573      * @return {Ext.Window} this
39574      */
39575     show : function(animateTarget, cb, scope){
39576         if(!this.rendered){
39577             this.render(Ext.getBody());
39578         }
39579         if(this.hidden === false){
39580             this.toFront();
39581             return this;
39582         }
39583         if(this.fireEvent('beforeshow', this) === false){
39584             return this;
39585         }
39586         if(cb){
39587             this.on('show', cb, scope, {single:true});
39588         }
39589         this.hidden = false;
39590         if(Ext.isDefined(animateTarget)){
39591             this.setAnimateTarget(animateTarget);
39592         }
39593         this.beforeShow();
39594         if(this.animateTarget){
39595             this.animShow();
39596         }else{
39597             this.afterShow();
39598         }
39599         return this;
39600     },
39601
39602     // private
39603     afterShow : function(isAnim){
39604         if (this.isDestroyed){
39605             return false;
39606         }
39607         this.proxy.hide();
39608         this.el.setStyle('display', 'block');
39609         this.el.show();
39610         if(this.maximized){
39611             this.fitContainer();
39612         }
39613         if(Ext.isMac && Ext.isGecko2){ // work around stupid FF 2.0/Mac scroll bar bug
39614             this.cascade(this.setAutoScroll);
39615         }
39616
39617         if(this.monitorResize || this.modal || this.constrain || this.constrainHeader){
39618             Ext.EventManager.onWindowResize(this.onWindowResize, this);
39619         }
39620         this.doConstrain();
39621         this.doLayout();
39622         if(this.keyMap){
39623             this.keyMap.enable();
39624         }
39625         this.toFront();
39626         this.updateHandles();
39627         if(isAnim && (Ext.isIE || Ext.isWebKit)){
39628             var sz = this.getSize();
39629             this.onResize(sz.width, sz.height);
39630         }
39631         this.onShow();
39632         this.fireEvent('show', this);
39633     },
39634
39635     // private
39636     animShow : function(){
39637         this.proxy.show();
39638         this.proxy.setBox(this.animateTarget.getBox());
39639         this.proxy.setOpacity(0);
39640         var b = this.getBox();
39641         this.el.setStyle('display', 'none');
39642         this.proxy.shift(Ext.apply(b, {
39643             callback: this.afterShow.createDelegate(this, [true], false),
39644             scope: this,
39645             easing: 'easeNone',
39646             duration: 0.25,
39647             opacity: 0.5
39648         }));
39649     },
39650
39651     /**
39652      * Hides the window, setting it to invisible and applying negative offsets.
39653      * @param {String/Element} animateTarget (optional) The target element or id to which the window should
39654      * animate while hiding (defaults to null with no animation)
39655      * @param {Function} callback (optional) A callback function to call after the window is hidden
39656      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the callback is executed. Defaults to this Window.
39657      * @return {Ext.Window} this
39658      */
39659     hide : function(animateTarget, cb, scope){
39660         if(this.hidden || this.fireEvent('beforehide', this) === false){
39661             return this;
39662         }
39663         if(cb){
39664             this.on('hide', cb, scope, {single:true});
39665         }
39666         this.hidden = true;
39667         if(animateTarget !== undefined){
39668             this.setAnimateTarget(animateTarget);
39669         }
39670         if(this.modal){
39671             this.mask.hide();
39672             Ext.getBody().removeClass('x-body-masked');
39673         }
39674         if(this.animateTarget){
39675             this.animHide();
39676         }else{
39677             this.el.hide();
39678             this.afterHide();
39679         }
39680         return this;
39681     },
39682
39683     // private
39684     afterHide : function(){
39685         this.proxy.hide();
39686         if(this.monitorResize || this.modal || this.constrain || this.constrainHeader){
39687             Ext.EventManager.removeResizeListener(this.onWindowResize, this);
39688         }
39689         if(this.keyMap){
39690             this.keyMap.disable();
39691         }
39692         this.onHide();
39693         this.fireEvent('hide', this);
39694     },
39695
39696     // private
39697     animHide : function(){
39698         this.proxy.setOpacity(0.5);
39699         this.proxy.show();
39700         var tb = this.getBox(false);
39701         this.proxy.setBox(tb);
39702         this.el.hide();
39703         this.proxy.shift(Ext.apply(this.animateTarget.getBox(), {
39704             callback: this.afterHide,
39705             scope: this,
39706             duration: 0.25,
39707             easing: 'easeNone',
39708             opacity: 0
39709         }));
39710     },
39711
39712     /**
39713      * Method that is called immediately before the <code>show</code> event is fired.
39714      * Defaults to <code>Ext.emptyFn</code>.
39715      */
39716     onShow : Ext.emptyFn,
39717
39718     /**
39719      * Method that is called immediately before the <code>hide</code> event is fired.
39720      * Defaults to <code>Ext.emptyFn</code>.
39721      */
39722     onHide : Ext.emptyFn,
39723
39724     // private
39725     onWindowResize : function(){
39726         if(this.maximized){
39727             this.fitContainer();
39728         }
39729         if(this.modal){
39730             this.mask.setSize('100%', '100%');
39731             var force = this.mask.dom.offsetHeight;
39732             this.mask.setSize(Ext.lib.Dom.getViewWidth(true), Ext.lib.Dom.getViewHeight(true));
39733         }
39734         this.doConstrain();
39735     },
39736
39737     // private
39738     doConstrain : function(){
39739         if(this.constrain || this.constrainHeader){
39740             var offsets;
39741             if(this.constrain){
39742                 offsets = {
39743                     right:this.el.shadowOffset,
39744                     left:this.el.shadowOffset,
39745                     bottom:this.el.shadowOffset
39746                 };
39747             }else {
39748                 var s = this.getSize();
39749                 offsets = {
39750                     right:-(s.width - 100),
39751                     bottom:-(s.height - 25)
39752                 };
39753             }
39754
39755             var xy = this.el.getConstrainToXY(this.container, true, offsets);
39756             if(xy){
39757                 this.setPosition(xy[0], xy[1]);
39758             }
39759         }
39760     },
39761
39762     // private - used for dragging
39763     ghost : function(cls){
39764         var ghost = this.createGhost(cls);
39765         var box = this.getBox(true);
39766         ghost.setLeftTop(box.x, box.y);
39767         ghost.setWidth(box.width);
39768         this.el.hide();
39769         this.activeGhost = ghost;
39770         return ghost;
39771     },
39772
39773     // private
39774     unghost : function(show, matchPosition){
39775         if(!this.activeGhost) {
39776             return;
39777         }
39778         if(show !== false){
39779             this.el.show();
39780             this.focus();
39781             if(Ext.isMac && Ext.isGecko2){ // work around stupid FF 2.0/Mac scroll bar bug
39782                 this.cascade(this.setAutoScroll);
39783             }
39784         }
39785         if(matchPosition !== false){
39786             this.setPosition(this.activeGhost.getLeft(true), this.activeGhost.getTop(true));
39787         }
39788         this.activeGhost.hide();
39789         this.activeGhost.remove();
39790         delete this.activeGhost;
39791     },
39792
39793     /**
39794      * Placeholder method for minimizing the window.  By default, this method simply fires the {@link #minimize} event
39795      * since the behavior of minimizing a window is application-specific.  To implement custom minimize behavior,
39796      * either the minimize event can be handled or this method can be overridden.
39797      * @return {Ext.Window} this
39798      */
39799     minimize : function(){
39800         this.fireEvent('minimize', this);
39801         return this;
39802     },
39803
39804     /**
39805      * <p>Closes the Window, removes it from the DOM, {@link Ext.Component#destroy destroy}s
39806      * the Window object and all its descendant Components. The {@link Ext.Panel#beforeclose beforeclose}
39807      * event is fired before the close happens and will cancel the close action if it returns false.<p>
39808      * <p><b>Note:</b> This method is not affected by the {@link #closeAction} setting which
39809      * only affects the action triggered when clicking the {@link #closable 'close' tool in the header}.
39810      * To hide the Window without destroying it, call {@link #hide}.</p>
39811      */
39812     close : function(){
39813         if(this.fireEvent('beforeclose', this) !== false){
39814             if(this.hidden){
39815                 this.doClose();
39816             }else{
39817                 this.hide(null, this.doClose, this);
39818             }
39819         }
39820     },
39821
39822     // private
39823     doClose : function(){
39824         this.fireEvent('close', this);
39825         this.destroy();
39826     },
39827
39828     /**
39829      * Fits the window within its current container and automatically replaces
39830      * the {@link #maximizable 'maximize' tool button} with the 'restore' tool button.
39831      * Also see {@link #toggleMaximize}.
39832      * @return {Ext.Window} this
39833      */
39834     maximize : function(){
39835         if(!this.maximized){
39836             this.expand(false);
39837             this.restoreSize = this.getSize();
39838             this.restorePos = this.getPosition(true);
39839             if (this.maximizable){
39840                 this.tools.maximize.hide();
39841                 this.tools.restore.show();
39842             }
39843             this.maximized = true;
39844             this.el.disableShadow();
39845
39846             if(this.dd){
39847                 this.dd.lock();
39848             }
39849             if(this.collapsible){
39850                 this.tools.toggle.hide();
39851             }
39852             this.el.addClass('x-window-maximized');
39853             this.container.addClass('x-window-maximized-ct');
39854
39855             this.setPosition(0, 0);
39856             this.fitContainer();
39857             this.fireEvent('maximize', this);
39858         }
39859         return this;
39860     },
39861
39862     /**
39863      * Restores a {@link #maximizable maximized}  window back to its original
39864      * size and position prior to being maximized and also replaces
39865      * the 'restore' tool button with the 'maximize' tool button.
39866      * Also see {@link #toggleMaximize}.
39867      * @return {Ext.Window} this
39868      */
39869     restore : function(){
39870         if(this.maximized){
39871             var t = this.tools;
39872             this.el.removeClass('x-window-maximized');
39873             if(t.restore){
39874                 t.restore.hide();
39875             }
39876             if(t.maximize){
39877                 t.maximize.show();
39878             }
39879             this.setPosition(this.restorePos[0], this.restorePos[1]);
39880             this.setSize(this.restoreSize.width, this.restoreSize.height);
39881             delete this.restorePos;
39882             delete this.restoreSize;
39883             this.maximized = false;
39884             this.el.enableShadow(true);
39885
39886             if(this.dd){
39887                 this.dd.unlock();
39888             }
39889             if(this.collapsible && t.toggle){
39890                 t.toggle.show();
39891             }
39892             this.container.removeClass('x-window-maximized-ct');
39893
39894             this.doConstrain();
39895             this.fireEvent('restore', this);
39896         }
39897         return this;
39898     },
39899
39900     /**
39901      * A shortcut method for toggling between {@link #maximize} and {@link #restore} based on the current maximized
39902      * state of the window.
39903      * @return {Ext.Window} this
39904      */
39905     toggleMaximize : function(){
39906         return this[this.maximized ? 'restore' : 'maximize']();
39907     },
39908
39909     // private
39910     fitContainer : function(){
39911         var vs = this.container.getViewSize(false);
39912         this.setSize(vs.width, vs.height);
39913     },
39914
39915     // private
39916     // z-index is managed by the WindowManager and may be overwritten at any time
39917     setZIndex : function(index){
39918         if(this.modal){
39919             this.mask.setStyle('z-index', index);
39920         }
39921         this.el.setZIndex(++index);
39922         index += 5;
39923
39924         if(this.resizer){
39925             this.resizer.proxy.setStyle('z-index', ++index);
39926         }
39927
39928         this.lastZIndex = index;
39929     },
39930
39931     /**
39932      * Aligns the window to the specified element
39933      * @param {Mixed} element The element to align to.
39934      * @param {String} position (optional, defaults to "tl-bl?") The position to align to (see {@link Ext.Element#alignTo} for more details).
39935      * @param {Array} offsets (optional) Offset the positioning by [x, y]
39936      * @return {Ext.Window} this
39937      */
39938     alignTo : function(element, position, offsets){
39939         var xy = this.el.getAlignToXY(element, position, offsets);
39940         this.setPagePosition(xy[0], xy[1]);
39941         return this;
39942     },
39943
39944     /**
39945      * Anchors this window to another element and realigns it when the window is resized or scrolled.
39946      * @param {Mixed} element The element to align to.
39947      * @param {String} position The position to align to (see {@link Ext.Element#alignTo} for more details)
39948      * @param {Array} offsets (optional) Offset the positioning by [x, y]
39949      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
39950      * is a number, it is used as the buffer delay (defaults to 50ms).
39951      * @return {Ext.Window} this
39952      */
39953     anchorTo : function(el, alignment, offsets, monitorScroll){
39954       if(this.doAnchor){
39955           Ext.EventManager.removeResizeListener(this.doAnchor, this);
39956           Ext.EventManager.un(window, 'scroll', this.doAnchor, this);
39957       }
39958       this.doAnchor = function(){
39959           this.alignTo(el, alignment, offsets);
39960       };
39961       Ext.EventManager.onWindowResize(this.doAnchor, this);
39962
39963       var tm = typeof monitorScroll;
39964       if(tm != 'undefined'){
39965           Ext.EventManager.on(window, 'scroll', this.doAnchor, this,
39966               {buffer: tm == 'number' ? monitorScroll : 50});
39967       }
39968       return this;
39969     },
39970
39971     /**
39972      * Brings this window to the front of any other visible windows
39973      * @param {Boolean} e (optional) Specify <tt>false</tt> to prevent the window from being focused.
39974      * @return {Ext.Window} this
39975      */
39976     toFront : function(e){
39977         if(this.manager.bringToFront(this)){
39978             if(!e || !e.getTarget().focus){
39979                 this.focus();
39980             }
39981         }
39982         return this;
39983     },
39984
39985     /**
39986      * Makes this the active window by showing its shadow, or deactivates it by hiding its shadow.  This method also
39987      * fires the {@link #activate} or {@link #deactivate} event depending on which action occurred. This method is
39988      * called internally by {@link Ext.WindowMgr}.
39989      * @param {Boolean} active True to activate the window, false to deactivate it (defaults to false)
39990      */
39991     setActive : function(active){
39992         if(active){
39993             if(!this.maximized){
39994                 this.el.enableShadow(true);
39995             }
39996             this.fireEvent('activate', this);
39997         }else{
39998             this.el.disableShadow();
39999             this.fireEvent('deactivate', this);
40000         }
40001     },
40002
40003     /**
40004      * Sends this window to the back of (lower z-index than) any other visible windows
40005      * @return {Ext.Window} this
40006      */
40007     toBack : function(){
40008         this.manager.sendToBack(this);
40009         return this;
40010     },
40011
40012     /**
40013      * Centers this window in the viewport
40014      * @return {Ext.Window} this
40015      */
40016     center : function(){
40017         var xy = this.el.getAlignToXY(this.container, 'c-c');
40018         this.setPagePosition(xy[0], xy[1]);
40019         return this;
40020     }
40021
40022     /**
40023      * @cfg {Boolean} autoWidth @hide
40024      **/
40025 });
40026 Ext.reg('window', Ext.Window);
40027
40028 // private - custom Window DD implementation
40029 Ext.Window.DD = function(win){
40030     this.win = win;
40031     Ext.Window.DD.superclass.constructor.call(this, win.el.id, 'WindowDD-'+win.id);
40032     this.setHandleElId(win.header.id);
40033     this.scroll = false;
40034 };
40035
40036 Ext.extend(Ext.Window.DD, Ext.dd.DD, {
40037     moveOnly:true,
40038     headerOffsets:[100, 25],
40039     startDrag : function(){
40040         var w = this.win;
40041         this.proxy = w.ghost();
40042         if(w.constrain !== false){
40043             var so = w.el.shadowOffset;
40044             this.constrainTo(w.container, {right: so, left: so, bottom: so});
40045         }else if(w.constrainHeader !== false){
40046             var s = this.proxy.getSize();
40047             this.constrainTo(w.container, {right: -(s.width-this.headerOffsets[0]), bottom: -(s.height-this.headerOffsets[1])});
40048         }
40049     },
40050     b4Drag : Ext.emptyFn,
40051
40052     onDrag : function(e){
40053         this.alignElWithMouse(this.proxy, e.getPageX(), e.getPageY());
40054     },
40055
40056     endDrag : function(e){
40057         this.win.unghost();
40058         this.win.saveState();
40059     }
40060 });
40061 /**
40062  * @class Ext.WindowGroup
40063  * An object that manages a group of {@link Ext.Window} instances and provides z-order management
40064  * and window activation behavior.
40065  * @constructor
40066  */
40067 Ext.WindowGroup = function(){
40068     var list = {};
40069     var accessList = [];
40070     var front = null;
40071
40072     // private
40073     var sortWindows = function(d1, d2){
40074         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
40075     };
40076
40077     // private
40078     var orderWindows = function(){
40079         var a = accessList, len = a.length;
40080         if(len > 0){
40081             a.sort(sortWindows);
40082             var seed = a[0].manager.zseed;
40083             for(var i = 0; i < len; i++){
40084                 var win = a[i];
40085                 if(win && !win.hidden){
40086                     win.setZIndex(seed + (i*10));
40087                 }
40088             }
40089         }
40090         activateLast();
40091     };
40092
40093     // private
40094     var setActiveWin = function(win){
40095         if(win != front){
40096             if(front){
40097                 front.setActive(false);
40098             }
40099             front = win;
40100             if(win){
40101                 win.setActive(true);
40102             }
40103         }
40104     };
40105
40106     // private
40107     var activateLast = function(){
40108         for(var i = accessList.length-1; i >=0; --i) {
40109             if(!accessList[i].hidden){
40110                 setActiveWin(accessList[i]);
40111                 return;
40112             }
40113         }
40114         // none to activate
40115         setActiveWin(null);
40116     };
40117
40118     return {
40119         /**
40120          * The starting z-index for windows in this WindowGroup (defaults to 9000)
40121          * @type Number The z-index value
40122          */
40123         zseed : 9000,
40124
40125         /**
40126          * <p>Registers a {@link Ext.Window Window} with this WindowManager. This should not
40127          * need to be called under normal circumstances. Windows are automatically registered
40128          * with a {@link Ext.Window#manager manager} at construction time.</p>
40129          * <p>Where this may be useful is moving Windows between two WindowManagers. For example,
40130          * to bring the Ext.MessageBox dialog under the same manager as the Desktop's
40131          * WindowManager in the desktop sample app:</p><code><pre>
40132 var msgWin = Ext.MessageBox.getDialog();
40133 MyDesktop.getDesktop().getManager().register(msgWin);
40134 </pre></code>
40135          * @param {Window} win The Window to register.
40136          */
40137         register : function(win){
40138             if(win.manager){
40139                 win.manager.unregister(win);
40140             }
40141             win.manager = this;
40142
40143             list[win.id] = win;
40144             accessList.push(win);
40145             win.on('hide', activateLast);
40146         },
40147
40148         /**
40149          * <p>Unregisters a {@link Ext.Window Window} from this WindowManager. This should not
40150          * need to be called. Windows are automatically unregistered upon destruction.
40151          * See {@link #register}.</p>
40152          * @param {Window} win The Window to unregister.
40153          */
40154         unregister : function(win){
40155             delete win.manager;
40156             delete list[win.id];
40157             win.un('hide', activateLast);
40158             accessList.remove(win);
40159         },
40160
40161         /**
40162          * Gets a registered window by id.
40163          * @param {String/Object} id The id of the window or a {@link Ext.Window} instance
40164          * @return {Ext.Window}
40165          */
40166         get : function(id){
40167             return typeof id == "object" ? id : list[id];
40168         },
40169
40170         /**
40171          * Brings the specified window to the front of any other active windows in this WindowGroup.
40172          * @param {String/Object} win The id of the window or a {@link Ext.Window} instance
40173          * @return {Boolean} True if the dialog was brought to the front, else false
40174          * if it was already in front
40175          */
40176         bringToFront : function(win){
40177             win = this.get(win);
40178             if(win != front){
40179                 win._lastAccess = new Date().getTime();
40180                 orderWindows();
40181                 return true;
40182             }
40183             return false;
40184         },
40185
40186         /**
40187          * Sends the specified window to the back of other active windows in this WindowGroup.
40188          * @param {String/Object} win The id of the window or a {@link Ext.Window} instance
40189          * @return {Ext.Window} The window
40190          */
40191         sendToBack : function(win){
40192             win = this.get(win);
40193             win._lastAccess = -(new Date().getTime());
40194             orderWindows();
40195             return win;
40196         },
40197
40198         /**
40199          * Hides all windows in this WindowGroup.
40200          */
40201         hideAll : function(){
40202             for(var id in list){
40203                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
40204                     list[id].hide();
40205                 }
40206             }
40207         },
40208
40209         /**
40210          * Gets the currently-active window in this WindowGroup.
40211          * @return {Ext.Window} The active window
40212          */
40213         getActive : function(){
40214             return front;
40215         },
40216
40217         /**
40218          * Returns zero or more windows in this WindowGroup using the custom search function passed to this method.
40219          * The function should accept a single {@link Ext.Window} reference as its only argument and should
40220          * return true if the window matches the search criteria, otherwise it should return false.
40221          * @param {Function} fn The search function
40222          * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the Window being tested.
40223          * that gets passed to the function if not specified)
40224          * @return {Array} An array of zero or more matching windows
40225          */
40226         getBy : function(fn, scope){
40227             var r = [];
40228             for(var i = accessList.length-1; i >=0; --i) {
40229                 var win = accessList[i];
40230                 if(fn.call(scope||win, win) !== false){
40231                     r.push(win);
40232                 }
40233             }
40234             return r;
40235         },
40236
40237         /**
40238          * Executes the specified function once for every window in this WindowGroup, passing each
40239          * window as the only parameter. Returning false from the function will stop the iteration.
40240          * @param {Function} fn The function to execute for each item
40241          * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the current Window in the iteration.
40242          */
40243         each : function(fn, scope){
40244             for(var id in list){
40245                 if(list[id] && typeof list[id] != "function"){
40246                     if(fn.call(scope || list[id], list[id]) === false){
40247                         return;
40248                     }
40249                 }
40250             }
40251         }
40252     };
40253 };
40254
40255
40256 /**
40257  * @class Ext.WindowMgr
40258  * @extends Ext.WindowGroup
40259  * The default global window group that is available automatically.  To have more than one group of windows
40260  * with separate z-order stacks, create additional instances of {@link Ext.WindowGroup} as needed.
40261  * @singleton
40262  */
40263 Ext.WindowMgr = new Ext.WindowGroup();/**\r
40264  * @class Ext.MessageBox\r
40265  * <p>Utility class for generating different styles of message boxes.  The alias Ext.Msg can also be used.<p/>\r
40266  * <p>Note that the MessageBox is asynchronous.  Unlike a regular JavaScript <code>alert</code> (which will halt\r
40267  * browser execution), showing a MessageBox will not cause the code to stop.  For this reason, if you have code\r
40268  * that should only run <em>after</em> some user feedback from the MessageBox, you must use a callback function\r
40269  * (see the <code>function</code> parameter for {@link #show} for more details).</p>\r
40270  * <p>Example usage:</p>\r
40271  *<pre><code>\r
40272 // Basic alert:\r
40273 Ext.Msg.alert('Status', 'Changes saved successfully.');\r
40274 \r
40275 // Prompt for user data and process the result using a callback:\r
40276 Ext.Msg.prompt('Name', 'Please enter your name:', function(btn, text){\r
40277     if (btn == 'ok'){\r
40278         // process text value and close...\r
40279     }\r
40280 });\r
40281 \r
40282 // Show a dialog using config options:\r
40283 Ext.Msg.show({\r
40284    title:'Save Changes?',\r
40285    msg: 'You are closing a tab that has unsaved changes. Would you like to save your changes?',\r
40286    buttons: Ext.Msg.YESNOCANCEL,\r
40287    fn: processResult,\r
40288    animEl: 'elId',\r
40289    icon: Ext.MessageBox.QUESTION\r
40290 });\r
40291 </code></pre>\r
40292  * @singleton\r
40293  */\r
40294 Ext.MessageBox = function(){\r
40295     var dlg, opt, mask, waitTimer,\r
40296         bodyEl, msgEl, textboxEl, textareaEl, progressBar, pp, iconEl, spacerEl,\r
40297         buttons, activeTextEl, bwidth, bufferIcon = '', iconCls = '',\r
40298         buttonNames = ['ok', 'yes', 'no', 'cancel'];\r
40299 \r
40300     // private\r
40301     var handleButton = function(button){\r
40302         buttons[button].blur();\r
40303         if(dlg.isVisible()){\r
40304             dlg.hide();\r
40305             handleHide();\r
40306             Ext.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value, opt], 1);\r
40307         }\r
40308     };\r
40309 \r
40310     // private\r
40311     var handleHide = function(){\r
40312         if(opt && opt.cls){\r
40313             dlg.el.removeClass(opt.cls);\r
40314         }\r
40315         progressBar.reset();        \r
40316     };\r
40317 \r
40318     // private\r
40319     var handleEsc = function(d, k, e){\r
40320         if(opt && opt.closable !== false){\r
40321             dlg.hide();\r
40322             handleHide();\r
40323         }\r
40324         if(e){\r
40325             e.stopEvent();\r
40326         }\r
40327     };\r
40328 \r
40329     // private\r
40330     var updateButtons = function(b){\r
40331         var width = 0,\r
40332             cfg;\r
40333         if(!b){\r
40334             Ext.each(buttonNames, function(name){\r
40335                 buttons[name].hide();\r
40336             });\r
40337             return width;\r
40338         }\r
40339         dlg.footer.dom.style.display = '';\r
40340         Ext.iterate(buttons, function(name, btn){\r
40341             cfg = b[name];\r
40342             if(cfg){\r
40343                 btn.show();\r
40344                 btn.setText(Ext.isString(cfg) ? cfg : Ext.MessageBox.buttonText[name]);\r
40345                 width += btn.getEl().getWidth() + 15;\r
40346             }else{\r
40347                 btn.hide();\r
40348             }\r
40349         });\r
40350         return width;\r
40351     };\r
40352 \r
40353     return {\r
40354         /**\r
40355          * Returns a reference to the underlying {@link Ext.Window} element\r
40356          * @return {Ext.Window} The window\r
40357          */\r
40358         getDialog : function(titleText){\r
40359            if(!dlg){\r
40360                 var btns = [];\r
40361                 \r
40362                 buttons = {};\r
40363                 Ext.each(buttonNames, function(name){\r
40364                     btns.push(buttons[name] = new Ext.Button({\r
40365                         text: this.buttonText[name],\r
40366                         handler: handleButton.createCallback(name),\r
40367                         hideMode: 'offsets'\r
40368                     }));\r
40369                 }, this);\r
40370                 dlg = new Ext.Window({\r
40371                     autoCreate : true,\r
40372                     title:titleText,\r
40373                     resizable:false,\r
40374                     constrain:true,\r
40375                     constrainHeader:true,\r
40376                     minimizable : false,\r
40377                     maximizable : false,\r
40378                     stateful: false,\r
40379                     modal: true,\r
40380                     shim:true,\r
40381                     buttonAlign:"center",\r
40382                     width:400,\r
40383                     height:100,\r
40384                     minHeight: 80,\r
40385                     plain:true,\r
40386                     footer:true,\r
40387                     closable:true,\r
40388                     close : function(){\r
40389                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){\r
40390                             handleButton("no");\r
40391                         }else{\r
40392                             handleButton("cancel");\r
40393                         }\r
40394                     },\r
40395                     fbar: new Ext.Toolbar({\r
40396                         items: btns,\r
40397                         enableOverflow: false\r
40398                     })\r
40399                 });\r
40400                 dlg.render(document.body);\r
40401                 dlg.getEl().addClass('x-window-dlg');\r
40402                 mask = dlg.mask;\r
40403                 bodyEl = dlg.body.createChild({\r
40404                     html:'<div class="ext-mb-icon"></div><div class="ext-mb-content"><span class="ext-mb-text"></span><br /><div class="ext-mb-fix-cursor"><input type="text" class="ext-mb-input" /><textarea class="ext-mb-textarea"></textarea></div></div>'\r
40405                 });\r
40406                 iconEl = Ext.get(bodyEl.dom.firstChild);\r
40407                 var contentEl = bodyEl.dom.childNodes[1];\r
40408                 msgEl = Ext.get(contentEl.firstChild);\r
40409                 textboxEl = Ext.get(contentEl.childNodes[2].firstChild);\r
40410                 textboxEl.enableDisplayMode();\r
40411                 textboxEl.addKeyListener([10,13], function(){\r
40412                     if(dlg.isVisible() && opt && opt.buttons){\r
40413                         if(opt.buttons.ok){\r
40414                             handleButton("ok");\r
40415                         }else if(opt.buttons.yes){\r
40416                             handleButton("yes");\r
40417                         }\r
40418                     }\r
40419                 });\r
40420                 textareaEl = Ext.get(contentEl.childNodes[2].childNodes[1]);\r
40421                 textareaEl.enableDisplayMode();\r
40422                 progressBar = new Ext.ProgressBar({\r
40423                     renderTo:bodyEl\r
40424                 });\r
40425                bodyEl.createChild({cls:'x-clear'});\r
40426             }\r
40427             return dlg;\r
40428         },\r
40429 \r
40430         /**\r
40431          * Updates the message box body text\r
40432          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to\r
40433          * the XHTML-compliant non-breaking space character '&amp;#160;')\r
40434          * @return {Ext.MessageBox} this\r
40435          */\r
40436         updateText : function(text){\r
40437             if(!dlg.isVisible() && !opt.width){\r
40438                 dlg.setSize(this.maxWidth, 100); // resize first so content is never clipped from previous shows\r
40439             }\r
40440             msgEl.update(text || '&#160;');\r
40441 \r
40442             var iw = iconCls != '' ? (iconEl.getWidth() + iconEl.getMargins('lr')) : 0,\r
40443                 mw = msgEl.getWidth() + msgEl.getMargins('lr'),\r
40444                 fw = dlg.getFrameWidth('lr'),\r
40445                 bw = dlg.body.getFrameWidth('lr'),\r
40446                 w;\r
40447                 \r
40448             if (Ext.isIE && iw > 0){\r
40449                 //3 pixels get subtracted in the icon CSS for an IE margin issue,\r
40450                 //so we have to add it back here for the overall width to be consistent\r
40451                 iw += 3;\r
40452             }\r
40453             w = Math.max(Math.min(opt.width || iw+mw+fw+bw, opt.maxWidth || this.maxWidth),\r
40454                     Math.max(opt.minWidth || this.minWidth, bwidth || 0));\r
40455 \r
40456             if(opt.prompt === true){\r
40457                 activeTextEl.setWidth(w-iw-fw-bw);\r
40458             }\r
40459             if(opt.progress === true || opt.wait === true){\r
40460                 progressBar.setSize(w-iw-fw-bw);\r
40461             }\r
40462             if(Ext.isIE && w == bwidth){\r
40463                 w += 4; //Add offset when the content width is smaller than the buttons.    \r
40464             }\r
40465             dlg.setSize(w, 'auto').center();\r
40466             return this;\r
40467         },\r
40468 \r
40469         /**\r
40470          * Updates a progress-style message box's text and progress bar. Only relevant on message boxes\r
40471          * initiated via {@link Ext.MessageBox#progress} or {@link Ext.MessageBox#wait},\r
40472          * or by calling {@link Ext.MessageBox#show} with progress: true.\r
40473          * @param {Number} value Any number between 0 and 1 (e.g., .5, defaults to 0)\r
40474          * @param {String} progressText The progress text to display inside the progress bar (defaults to '')\r
40475          * @param {String} msg The message box's body text is replaced with the specified string (defaults to undefined\r
40476          * so that any existing body text will not get overwritten by default unless a new value is passed in)\r
40477          * @return {Ext.MessageBox} this\r
40478          */\r
40479         updateProgress : function(value, progressText, msg){\r
40480             progressBar.updateProgress(value, progressText);\r
40481             if(msg){\r
40482                 this.updateText(msg);\r
40483             }\r
40484             return this;\r
40485         },\r
40486 \r
40487         /**\r
40488          * Returns true if the message box is currently displayed\r
40489          * @return {Boolean} True if the message box is visible, else false\r
40490          */\r
40491         isVisible : function(){\r
40492             return dlg && dlg.isVisible();\r
40493         },\r
40494 \r
40495         /**\r
40496          * Hides the message box if it is displayed\r
40497          * @return {Ext.MessageBox} this\r
40498          */\r
40499         hide : function(){\r
40500             var proxy = dlg ? dlg.activeGhost : null;\r
40501             if(this.isVisible() || proxy){\r
40502                 dlg.hide();\r
40503                 handleHide();\r
40504                 if (proxy){\r
40505                     // unghost is a private function, but i saw no better solution\r
40506                     // to fix the locking problem when dragging while it closes\r
40507                     dlg.unghost(false, false);\r
40508                 } \r
40509             }\r
40510             return this;\r
40511         },\r
40512 \r
40513         /**\r
40514          * Displays a new message box, or reinitializes an existing message box, based on the config options\r
40515          * passed in. All display functions (e.g. prompt, alert, etc.) on MessageBox call this function internally,\r
40516          * although those calls are basic shortcuts and do not support all of the config options allowed here.\r
40517          * @param {Object} config The following config options are supported: <ul>\r
40518          * <li><b>animEl</b> : String/Element<div class="sub-desc">An id or Element from which the message box should animate as it\r
40519          * opens and closes (defaults to undefined)</div></li>\r
40520          * <li><b>buttons</b> : Object/Boolean<div class="sub-desc">A button config object (e.g., Ext.MessageBox.OKCANCEL or {ok:'Foo',\r
40521          * cancel:'Bar'}), or false to not show any buttons (defaults to false)</div></li>\r
40522          * <li><b>closable</b> : Boolean<div class="sub-desc">False to hide the top-right close button (defaults to true). Note that\r
40523          * progress and wait dialogs will ignore this property and always hide the close button as they can only\r
40524          * be closed programmatically.</div></li>\r
40525          * <li><b>cls</b> : String<div class="sub-desc">A custom CSS class to apply to the message box's container element</div></li>\r
40526          * <li><b>defaultTextHeight</b> : Number<div class="sub-desc">The default height in pixels of the message box's multiline textarea\r
40527          * if displayed (defaults to 75)</div></li>\r
40528          * <li><b>fn</b> : Function<div class="sub-desc">A callback function which is called when the dialog is dismissed either\r
40529          * by clicking on the configured buttons, or on the dialog close button, or by pressing\r
40530          * the return button to enter input.\r
40531          * <p>Progress and wait dialogs will ignore this option since they do not respond to user\r
40532          * actions and can only be closed programmatically, so any required function should be called\r
40533          * by the same code after it closes the dialog. Parameters passed:<ul>\r
40534          * <li><b>buttonId</b> : String<div class="sub-desc">The ID of the button pressed, one of:<div class="sub-desc"><ul>\r
40535          * <li><tt>ok</tt></li>\r
40536          * <li><tt>yes</tt></li>\r
40537          * <li><tt>no</tt></li>\r
40538          * <li><tt>cancel</tt></li>\r
40539          * </ul></div></div></li>\r
40540          * <li><b>text</b> : String<div class="sub-desc">Value of the input field if either <tt><a href="#show-option-prompt" ext:member="show-option-prompt" ext:cls="Ext.MessageBox">prompt</a></tt>\r
40541          * or <tt><a href="#show-option-multiline" ext:member="show-option-multiline" ext:cls="Ext.MessageBox">multiline</a></tt> is true</div></li>\r
40542          * <li><b>opt</b> : Object<div class="sub-desc">The config object passed to show.</div></li>\r
40543          * </ul></p></div></li>\r
40544          * <li><b>scope</b> : Object<div class="sub-desc">The scope of the callback function</div></li>\r
40545          * <li><b>icon</b> : String<div class="sub-desc">A CSS class that provides a background image to be used as the body icon for the\r
40546          * dialog (e.g. Ext.MessageBox.WARNING or 'custom-class') (defaults to '')</div></li>\r
40547          * <li><b>iconCls</b> : String<div class="sub-desc">The standard {@link Ext.Window#iconCls} to\r
40548          * add an optional header icon (defaults to '')</div></li>\r
40549          * <li><b>maxWidth</b> : Number<div class="sub-desc">The maximum width in pixels of the message box (defaults to 600)</div></li>\r
40550          * <li><b>minWidth</b> : Number<div class="sub-desc">The minimum width in pixels of the message box (defaults to 100)</div></li>\r
40551          * <li><b>modal</b> : Boolean<div class="sub-desc">False to allow user interaction with the page while the message box is\r
40552          * displayed (defaults to true)</div></li>\r
40553          * <li><b>msg</b> : String<div class="sub-desc">A string that will replace the existing message box body text (defaults to the\r
40554          * XHTML-compliant non-breaking space character '&amp;#160;')</div></li>\r
40555          * <li><a id="show-option-multiline"></a><b>multiline</b> : Boolean<div class="sub-desc">\r
40556          * True to prompt the user to enter multi-line text (defaults to false)</div></li>\r
40557          * <li><b>progress</b> : Boolean<div class="sub-desc">True to display a progress bar (defaults to false)</div></li>\r
40558          * <li><b>progressText</b> : String<div class="sub-desc">The text to display inside the progress bar if progress = true (defaults to '')</div></li>\r
40559          * <li><a id="show-option-prompt"></a><b>prompt</b> : Boolean<div class="sub-desc">True to prompt the user to enter single-line text (defaults to false)</div></li>\r
40560          * <li><b>proxyDrag</b> : Boolean<div class="sub-desc">True to display a lightweight proxy while dragging (defaults to false)</div></li>\r
40561          * <li><b>title</b> : String<div class="sub-desc">The title text</div></li>\r
40562          * <li><b>value</b> : String<div class="sub-desc">The string value to set into the active textbox element if displayed</div></li>\r
40563          * <li><b>wait</b> : Boolean<div class="sub-desc">True to display a progress bar (defaults to false)</div></li>\r
40564          * <li><b>waitConfig</b> : Object<div class="sub-desc">A {@link Ext.ProgressBar#waitConfig} object (applies only if wait = true)</div></li>\r
40565          * <li><b>width</b> : Number<div class="sub-desc">The width of the dialog in pixels</div></li>\r
40566          * </ul>\r
40567          * Example usage:\r
40568          * <pre><code>\r
40569 Ext.Msg.show({\r
40570    title: 'Address',\r
40571    msg: 'Please enter your address:',\r
40572    width: 300,\r
40573    buttons: Ext.MessageBox.OKCANCEL,\r
40574    multiline: true,\r
40575    fn: saveAddress,\r
40576    animEl: 'addAddressBtn',\r
40577    icon: Ext.MessageBox.INFO\r
40578 });\r
40579 </code></pre>\r
40580          * @return {Ext.MessageBox} this\r
40581          */\r
40582         show : function(options){\r
40583             if(this.isVisible()){\r
40584                 this.hide();\r
40585             }\r
40586             opt = options;\r
40587             var d = this.getDialog(opt.title || "&#160;");\r
40588 \r
40589             d.setTitle(opt.title || "&#160;");\r
40590             var allowClose = (opt.closable !== false && opt.progress !== true && opt.wait !== true);\r
40591             d.tools.close.setDisplayed(allowClose);\r
40592             activeTextEl = textboxEl;\r
40593             opt.prompt = opt.prompt || (opt.multiline ? true : false);\r
40594             if(opt.prompt){\r
40595                 if(opt.multiline){\r
40596                     textboxEl.hide();\r
40597                     textareaEl.show();\r
40598                     textareaEl.setHeight(Ext.isNumber(opt.multiline) ? opt.multiline : this.defaultTextHeight);\r
40599                     activeTextEl = textareaEl;\r
40600                 }else{\r
40601                     textboxEl.show();\r
40602                     textareaEl.hide();\r
40603                 }\r
40604             }else{\r
40605                 textboxEl.hide();\r
40606                 textareaEl.hide();\r
40607             }\r
40608             activeTextEl.dom.value = opt.value || "";\r
40609             if(opt.prompt){\r
40610                 d.focusEl = activeTextEl;\r
40611             }else{\r
40612                 var bs = opt.buttons;\r
40613                 var db = null;\r
40614                 if(bs && bs.ok){\r
40615                     db = buttons["ok"];\r
40616                 }else if(bs && bs.yes){\r
40617                     db = buttons["yes"];\r
40618                 }\r
40619                 if (db){\r
40620                     d.focusEl = db;\r
40621                 }\r
40622             }\r
40623             if(opt.iconCls){\r
40624               d.setIconClass(opt.iconCls);\r
40625             }\r
40626             this.setIcon(Ext.isDefined(opt.icon) ? opt.icon : bufferIcon);\r
40627             bwidth = updateButtons(opt.buttons);\r
40628             progressBar.setVisible(opt.progress === true || opt.wait === true);\r
40629             this.updateProgress(0, opt.progressText);\r
40630             this.updateText(opt.msg);\r
40631             if(opt.cls){\r
40632                 d.el.addClass(opt.cls);\r
40633             }\r
40634             d.proxyDrag = opt.proxyDrag === true;\r
40635             d.modal = opt.modal !== false;\r
40636             d.mask = opt.modal !== false ? mask : false;\r
40637             if(!d.isVisible()){\r
40638                 // force it to the end of the z-index stack so it gets a cursor in FF\r
40639                 document.body.appendChild(dlg.el.dom);\r
40640                 d.setAnimateTarget(opt.animEl);\r
40641                 //workaround for window internally enabling keymap in afterShow\r
40642                 d.on('show', function(){\r
40643                     if(allowClose === true){\r
40644                         d.keyMap.enable();\r
40645                     }else{\r
40646                         d.keyMap.disable();\r
40647                     }\r
40648                 }, this, {single:true});\r
40649                 d.show(opt.animEl);\r
40650             }\r
40651             if(opt.wait === true){\r
40652                 progressBar.wait(opt.waitConfig);\r
40653             }\r
40654             return this;\r
40655         },\r
40656 \r
40657         /**\r
40658          * Adds the specified icon to the dialog.  By default, the class 'ext-mb-icon' is applied for default\r
40659          * styling, and the class passed in is expected to supply the background image url. Pass in empty string ('')\r
40660          * to clear any existing icon. This method must be called before the MessageBox is shown.\r
40661          * The following built-in icon classes are supported, but you can also pass in a custom class name:\r
40662          * <pre>\r
40663 Ext.MessageBox.INFO\r
40664 Ext.MessageBox.WARNING\r
40665 Ext.MessageBox.QUESTION\r
40666 Ext.MessageBox.ERROR\r
40667          *</pre>\r
40668          * @param {String} icon A CSS classname specifying the icon's background image url, or empty string to clear the icon\r
40669          * @return {Ext.MessageBox} this\r
40670          */\r
40671         setIcon : function(icon){\r
40672             if(!dlg){\r
40673                 bufferIcon = icon;\r
40674                 return;\r
40675             }\r
40676             bufferIcon = undefined;\r
40677             if(icon && icon != ''){\r
40678                 iconEl.removeClass('x-hidden');\r
40679                 iconEl.replaceClass(iconCls, icon);\r
40680                 bodyEl.addClass('x-dlg-icon');\r
40681                 iconCls = icon;\r
40682             }else{\r
40683                 iconEl.replaceClass(iconCls, 'x-hidden');\r
40684                 bodyEl.removeClass('x-dlg-icon');\r
40685                 iconCls = '';\r
40686             }\r
40687             return this;\r
40688         },\r
40689 \r
40690         /**\r
40691          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by\r
40692          * the user.  You are responsible for updating the progress bar as needed via {@link Ext.MessageBox#updateProgress}\r
40693          * and closing the message box when the process is complete.\r
40694          * @param {String} title The title bar text\r
40695          * @param {String} msg The message box body text\r
40696          * @param {String} progressText (optional) The text to display inside the progress bar (defaults to '')\r
40697          * @return {Ext.MessageBox} this\r
40698          */\r
40699         progress : function(title, msg, progressText){\r
40700             this.show({\r
40701                 title : title,\r
40702                 msg : msg,\r
40703                 buttons: false,\r
40704                 progress:true,\r
40705                 closable:false,\r
40706                 minWidth: this.minProgressWidth,\r
40707                 progressText: progressText\r
40708             });\r
40709             return this;\r
40710         },\r
40711 \r
40712         /**\r
40713          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user\r
40714          * interaction while waiting for a long-running process to complete that does not have defined intervals.\r
40715          * You are responsible for closing the message box when the process is complete.\r
40716          * @param {String} msg The message box body text\r
40717          * @param {String} title (optional) The title bar text\r
40718          * @param {Object} config (optional) A {@link Ext.ProgressBar#waitConfig} object\r
40719          * @return {Ext.MessageBox} this\r
40720          */\r
40721         wait : function(msg, title, config){\r
40722             this.show({\r
40723                 title : title,\r
40724                 msg : msg,\r
40725                 buttons: false,\r
40726                 closable:false,\r
40727                 wait:true,\r
40728                 modal:true,\r
40729                 minWidth: this.minProgressWidth,\r
40730                 waitConfig: config\r
40731             });\r
40732             return this;\r
40733         },\r
40734 \r
40735         /**\r
40736          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript alert prompt).\r
40737          * If a callback function is passed it will be called after the user clicks the button, and the\r
40738          * id of the button that was clicked will be passed as the only parameter to the callback\r
40739          * (could also be the top-right close button).\r
40740          * @param {String} title The title bar text\r
40741          * @param {String} msg The message box body text\r
40742          * @param {Function} fn (optional) The callback function invoked after the message box is closed\r
40743          * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the callback is executed. Defaults to the browser wnidow.\r
40744          * @return {Ext.MessageBox} this\r
40745          */\r
40746         alert : function(title, msg, fn, scope){\r
40747             this.show({\r
40748                 title : title,\r
40749                 msg : msg,\r
40750                 buttons: this.OK,\r
40751                 fn: fn,\r
40752                 scope : scope,\r
40753                 minWidth: this.minWidth\r
40754             });\r
40755             return this;\r
40756         },\r
40757 \r
40758         /**\r
40759          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's confirm).\r
40760          * If a callback function is passed it will be called after the user clicks either button,\r
40761          * and the id of the button that was clicked will be passed as the only parameter to the callback\r
40762          * (could also be the top-right close button).\r
40763          * @param {String} title The title bar text\r
40764          * @param {String} msg The message box body text\r
40765          * @param {Function} fn (optional) The callback function invoked after the message box is closed\r
40766          * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the callback is executed. Defaults to the browser wnidow.\r
40767          * @return {Ext.MessageBox} this\r
40768          */\r
40769         confirm : function(title, msg, fn, scope){\r
40770             this.show({\r
40771                 title : title,\r
40772                 msg : msg,\r
40773                 buttons: this.YESNO,\r
40774                 fn: fn,\r
40775                 scope : scope,\r
40776                 icon: this.QUESTION,\r
40777                 minWidth: this.minWidth\r
40778             });\r
40779             return this;\r
40780         },\r
40781 \r
40782         /**\r
40783          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to JavaScript's prompt).\r
40784          * The prompt can be a single-line or multi-line textbox.  If a callback function is passed it will be called after the user\r
40785          * clicks either button, and the id of the button that was clicked (could also be the top-right\r
40786          * close button) and the text that was entered will be passed as the two parameters to the callback.\r
40787          * @param {String} title The title bar text\r
40788          * @param {String} msg The message box body text\r
40789          * @param {Function} fn (optional) The callback function invoked after the message box is closed\r
40790          * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the callback is executed. Defaults to the browser wnidow.\r
40791          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight\r
40792          * property, or the height in pixels to create the textbox (defaults to false / single-line)\r
40793          * @param {String} value (optional) Default value of the text input element (defaults to '')\r
40794          * @return {Ext.MessageBox} this\r
40795          */\r
40796         prompt : function(title, msg, fn, scope, multiline, value){\r
40797             this.show({\r
40798                 title : title,\r
40799                 msg : msg,\r
40800                 buttons: this.OKCANCEL,\r
40801                 fn: fn,\r
40802                 minWidth: this.minPromptWidth,\r
40803                 scope : scope,\r
40804                 prompt:true,\r
40805                 multiline: multiline,\r
40806                 value: value\r
40807             });\r
40808             return this;\r
40809         },\r
40810 \r
40811         /**\r
40812          * Button config that displays a single OK button\r
40813          * @type Object\r
40814          */\r
40815         OK : {ok:true},\r
40816         /**\r
40817          * Button config that displays a single Cancel button\r
40818          * @type Object\r
40819          */\r
40820         CANCEL : {cancel:true},\r
40821         /**\r
40822          * Button config that displays OK and Cancel buttons\r
40823          * @type Object\r
40824          */\r
40825         OKCANCEL : {ok:true, cancel:true},\r
40826         /**\r
40827          * Button config that displays Yes and No buttons\r
40828          * @type Object\r
40829          */\r
40830         YESNO : {yes:true, no:true},\r
40831         /**\r
40832          * Button config that displays Yes, No and Cancel buttons\r
40833          * @type Object\r
40834          */\r
40835         YESNOCANCEL : {yes:true, no:true, cancel:true},\r
40836         /**\r
40837          * The CSS class that provides the INFO icon image\r
40838          * @type String\r
40839          */\r
40840         INFO : 'ext-mb-info',\r
40841         /**\r
40842          * The CSS class that provides the WARNING icon image\r
40843          * @type String\r
40844          */\r
40845         WARNING : 'ext-mb-warning',\r
40846         /**\r
40847          * The CSS class that provides the QUESTION icon image\r
40848          * @type String\r
40849          */\r
40850         QUESTION : 'ext-mb-question',\r
40851         /**\r
40852          * The CSS class that provides the ERROR icon image\r
40853          * @type String\r
40854          */\r
40855         ERROR : 'ext-mb-error',\r
40856 \r
40857         /**\r
40858          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)\r
40859          * @type Number\r
40860          */\r
40861         defaultTextHeight : 75,\r
40862         /**\r
40863          * The maximum width in pixels of the message box (defaults to 600)\r
40864          * @type Number\r
40865          */\r
40866         maxWidth : 600,\r
40867         /**\r
40868          * The minimum width in pixels of the message box (defaults to 100)\r
40869          * @type Number\r
40870          */\r
40871         minWidth : 100,\r
40872         /**\r
40873          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful\r
40874          * for setting a different minimum width than text-only dialogs may need (defaults to 250).\r
40875          * @type Number\r
40876          */\r
40877         minProgressWidth : 250,\r
40878         /**\r
40879          * The minimum width in pixels of the message box if it is a prompt dialog.  This is useful\r
40880          * for setting a different minimum width than text-only dialogs may need (defaults to 250).\r
40881          * @type Number\r
40882          */\r
40883         minPromptWidth: 250,\r
40884         /**\r
40885          * An object containing the default button text strings that can be overriden for localized language support.\r
40886          * Supported properties are: ok, cancel, yes and no.  Generally you should include a locale-specific\r
40887          * resource file for handling language support across the framework.\r
40888          * Customize the default text like so: Ext.MessageBox.buttonText.yes = "oui"; //french\r
40889          * @type Object\r
40890          */\r
40891         buttonText : {\r
40892             ok : "OK",\r
40893             cancel : "Cancel",\r
40894             yes : "Yes",\r
40895             no : "No"\r
40896         }\r
40897     };\r
40898 }();\r
40899 \r
40900 /**\r
40901  * Shorthand for {@link Ext.MessageBox}\r
40902  */\r
40903 Ext.Msg = Ext.MessageBox;/**\r
40904  * @class Ext.dd.PanelProxy\r
40905  * A custom drag proxy implementation specific to {@link Ext.Panel}s. This class is primarily used internally\r
40906  * for the Panel's drag drop implementation, and should never need to be created directly.\r
40907  * @constructor\r
40908  * @param panel The {@link Ext.Panel} to proxy for\r
40909  * @param config Configuration options\r
40910  */\r
40911 Ext.dd.PanelProxy = function(panel, config){\r
40912     this.panel = panel;\r
40913     this.id = this.panel.id +'-ddproxy';\r
40914     Ext.apply(this, config);\r
40915 };\r
40916 \r
40917 Ext.dd.PanelProxy.prototype = {\r
40918     /**\r
40919      * @cfg {Boolean} insertProxy True to insert a placeholder proxy element while dragging the panel,\r
40920      * false to drag with no proxy (defaults to true).\r
40921      */\r
40922     insertProxy : true,\r
40923 \r
40924     // private overrides\r
40925     setStatus : Ext.emptyFn,\r
40926     reset : Ext.emptyFn,\r
40927     update : Ext.emptyFn,\r
40928     stop : Ext.emptyFn,\r
40929     sync: Ext.emptyFn,\r
40930 \r
40931     /**\r
40932      * Gets the proxy's element\r
40933      * @return {Element} The proxy's element\r
40934      */\r
40935     getEl : function(){\r
40936         return this.ghost;\r
40937     },\r
40938 \r
40939     /**\r
40940      * Gets the proxy's ghost element\r
40941      * @return {Element} The proxy's ghost element\r
40942      */\r
40943     getGhost : function(){\r
40944         return this.ghost;\r
40945     },\r
40946 \r
40947     /**\r
40948      * Gets the proxy's element\r
40949      * @return {Element} The proxy's element\r
40950      */\r
40951     getProxy : function(){\r
40952         return this.proxy;\r
40953     },\r
40954 \r
40955     /**\r
40956      * Hides the proxy\r
40957      */\r
40958     hide : function(){\r
40959         if(this.ghost){\r
40960             if(this.proxy){\r
40961                 this.proxy.remove();\r
40962                 delete this.proxy;\r
40963             }\r
40964             this.panel.el.dom.style.display = '';\r
40965             this.ghost.remove();\r
40966             delete this.ghost;\r
40967         }\r
40968     },\r
40969 \r
40970     /**\r
40971      * Shows the proxy\r
40972      */\r
40973     show : function(){\r
40974         if(!this.ghost){\r
40975             this.ghost = this.panel.createGhost(undefined, undefined, Ext.getBody());\r
40976             this.ghost.setXY(this.panel.el.getXY())\r
40977             if(this.insertProxy){\r
40978                 this.proxy = this.panel.el.insertSibling({cls:'x-panel-dd-spacer'});\r
40979                 this.proxy.setSize(this.panel.getSize());\r
40980             }\r
40981             this.panel.el.dom.style.display = 'none';\r
40982         }\r
40983     },\r
40984 \r
40985     // private\r
40986     repair : function(xy, callback, scope){\r
40987         this.hide();\r
40988         if(typeof callback == "function"){\r
40989             callback.call(scope || this);\r
40990         }\r
40991     },\r
40992 \r
40993     /**\r
40994      * Moves the proxy to a different position in the DOM.  This is typically called while dragging the Panel\r
40995      * to keep the proxy sync'd to the Panel's location.\r
40996      * @param {HTMLElement} parentNode The proxy's parent DOM node\r
40997      * @param {HTMLElement} before (optional) The sibling node before which the proxy should be inserted (defaults\r
40998      * to the parent's last child if not specified)\r
40999      */\r
41000     moveProxy : function(parentNode, before){\r
41001         if(this.proxy){\r
41002             parentNode.insertBefore(this.proxy.dom, before);\r
41003         }\r
41004     }\r
41005 };\r
41006 \r
41007 // private - DD implementation for Panels\r
41008 Ext.Panel.DD = function(panel, cfg){\r
41009     this.panel = panel;\r
41010     this.dragData = {panel: panel};\r
41011     this.proxy = new Ext.dd.PanelProxy(panel, cfg);\r
41012     Ext.Panel.DD.superclass.constructor.call(this, panel.el, cfg);\r
41013     var h = panel.header;\r
41014     if(h){\r
41015         this.setHandleElId(h.id);\r
41016     }\r
41017     (h ? h : this.panel.body).setStyle('cursor', 'move');\r
41018     this.scroll = false;\r
41019 };\r
41020 \r
41021 Ext.extend(Ext.Panel.DD, Ext.dd.DragSource, {\r
41022     showFrame: Ext.emptyFn,\r
41023     startDrag: Ext.emptyFn,\r
41024     b4StartDrag: function(x, y) {\r
41025         this.proxy.show();\r
41026     },\r
41027     b4MouseDown: function(e) {\r
41028         var x = e.getPageX();\r
41029         var y = e.getPageY();\r
41030         this.autoOffset(x, y);\r
41031     },\r
41032     onInitDrag : function(x, y){\r
41033         this.onStartDrag(x, y);\r
41034         return true;\r
41035     },\r
41036     createFrame : Ext.emptyFn,\r
41037     getDragEl : function(e){\r
41038         return this.proxy.ghost.dom;\r
41039     },\r
41040     endDrag : function(e){\r
41041         this.proxy.hide();\r
41042         this.panel.saveState();\r
41043     },\r
41044 \r
41045     autoOffset : function(x, y) {\r
41046         x -= this.startPageX;\r
41047         y -= this.startPageY;\r
41048         this.setDelta(x, y);\r
41049     }\r
41050 });/**
41051  * @class Ext.state.Provider
41052  * Abstract base class for state provider implementations. This class provides methods
41053  * for encoding and decoding <b>typed</b> variables including dates and defines the
41054  * Provider interface.
41055  */
41056 Ext.state.Provider = function(){
41057     /**
41058      * @event statechange
41059      * Fires when a state change occurs.
41060      * @param {Provider} this This state provider
41061      * @param {String} key The state key which was changed
41062      * @param {String} value The encoded value for the state
41063      */
41064     this.addEvents("statechange");
41065     this.state = {};
41066     Ext.state.Provider.superclass.constructor.call(this);
41067 };
41068 Ext.extend(Ext.state.Provider, Ext.util.Observable, {
41069     /**
41070      * Returns the current value for a key
41071      * @param {String} name The key name
41072      * @param {Mixed} defaultValue A default value to return if the key's value is not found
41073      * @return {Mixed} The state data
41074      */
41075     get : function(name, defaultValue){
41076         return typeof this.state[name] == "undefined" ?
41077             defaultValue : this.state[name];
41078     },
41079
41080     /**
41081      * Clears a value from the state
41082      * @param {String} name The key name
41083      */
41084     clear : function(name){
41085         delete this.state[name];
41086         this.fireEvent("statechange", this, name, null);
41087     },
41088
41089     /**
41090      * Sets the value for a key
41091      * @param {String} name The key name
41092      * @param {Mixed} value The value to set
41093      */
41094     set : function(name, value){
41095         this.state[name] = value;
41096         this.fireEvent("statechange", this, name, value);
41097     },
41098
41099     /**
41100      * Decodes a string previously encoded with {@link #encodeValue}.
41101      * @param {String} value The value to decode
41102      * @return {Mixed} The decoded value
41103      */
41104     decodeValue : function(cookie){
41105         var re = /^(a|n|d|b|s|o)\:(.*)$/;
41106         var matches = re.exec(unescape(cookie));
41107         if(!matches || !matches[1]) return; // non state cookie
41108         var type = matches[1];
41109         var v = matches[2];
41110         switch(type){
41111             case "n":
41112                 return parseFloat(v);
41113             case "d":
41114                 return new Date(Date.parse(v));
41115             case "b":
41116                 return (v == "1");
41117             case "a":
41118                 var all = [];
41119                 if(v != ''){
41120                     Ext.each(v.split('^'), function(val){
41121                         all.push(this.decodeValue(val));
41122                     }, this);
41123                 }
41124                 return all;
41125            case "o":
41126                 var all = {};
41127                 if(v != ''){
41128                     Ext.each(v.split('^'), function(val){
41129                         var kv = val.split('=');
41130                         all[kv[0]] = this.decodeValue(kv[1]);
41131                     }, this);
41132                 }
41133                 return all;
41134            default:
41135                 return v;
41136         }
41137     },
41138
41139     /**
41140      * Encodes a value including type information.  Decode with {@link #decodeValue}.
41141      * @param {Mixed} value The value to encode
41142      * @return {String} The encoded value
41143      */
41144     encodeValue : function(v){
41145         var enc;
41146         if(typeof v == "number"){
41147             enc = "n:" + v;
41148         }else if(typeof v == "boolean"){
41149             enc = "b:" + (v ? "1" : "0");
41150         }else if(Ext.isDate(v)){
41151             enc = "d:" + v.toGMTString();
41152         }else if(Ext.isArray(v)){
41153             var flat = "";
41154             for(var i = 0, len = v.length; i < len; i++){
41155                 flat += this.encodeValue(v[i]);
41156                 if(i != len-1) flat += "^";
41157             }
41158             enc = "a:" + flat;
41159         }else if(typeof v == "object"){
41160             var flat = "";
41161             for(var key in v){
41162                 if(typeof v[key] != "function" && v[key] !== undefined){
41163                     flat += key + "=" + this.encodeValue(v[key]) + "^";
41164                 }
41165             }
41166             enc = "o:" + flat.substring(0, flat.length-1);
41167         }else{
41168             enc = "s:" + v;
41169         }
41170         return escape(enc);
41171     }
41172 });
41173 /**\r
41174  * @class Ext.state.Manager\r
41175  * This is the global state manager. By default all components that are "state aware" check this class\r
41176  * for state information if you don't pass them a custom state provider. In order for this class\r
41177  * to be useful, it must be initialized with a provider when your application initializes. Example usage:\r
41178  <pre><code>\r
41179 // in your initialization function\r
41180 init : function(){\r
41181    Ext.state.Manager.setProvider(new Ext.state.CookieProvider());\r
41182    var win = new Window(...);\r
41183    win.restoreState();\r
41184 }\r
41185  </code></pre>\r
41186  * @singleton\r
41187  */\r
41188 Ext.state.Manager = function(){\r
41189     var provider = new Ext.state.Provider();\r
41190 \r
41191     return {\r
41192         /**\r
41193          * Configures the default state provider for your application\r
41194          * @param {Provider} stateProvider The state provider to set\r
41195          */\r
41196         setProvider : function(stateProvider){\r
41197             provider = stateProvider;\r
41198         },\r
41199 \r
41200         /**\r
41201          * Returns the current value for a key\r
41202          * @param {String} name The key name\r
41203          * @param {Mixed} defaultValue The default value to return if the key lookup does not match\r
41204          * @return {Mixed} The state data\r
41205          */\r
41206         get : function(key, defaultValue){\r
41207             return provider.get(key, defaultValue);\r
41208         },\r
41209 \r
41210         /**\r
41211          * Sets the value for a key\r
41212          * @param {String} name The key name\r
41213          * @param {Mixed} value The state data\r
41214          */\r
41215          set : function(key, value){\r
41216             provider.set(key, value);\r
41217         },\r
41218 \r
41219         /**\r
41220          * Clears a value from the state\r
41221          * @param {String} name The key name\r
41222          */\r
41223         clear : function(key){\r
41224             provider.clear(key);\r
41225         },\r
41226 \r
41227         /**\r
41228          * Gets the currently configured state provider\r
41229          * @return {Provider} The state provider\r
41230          */\r
41231         getProvider : function(){\r
41232             return provider;\r
41233         }\r
41234     };\r
41235 }();\r
41236 /**\r
41237  * @class Ext.state.CookieProvider\r
41238  * @extends Ext.state.Provider\r
41239  * The default Provider implementation which saves state via cookies.\r
41240  * <br />Usage:\r
41241  <pre><code>\r
41242    var cp = new Ext.state.CookieProvider({\r
41243        path: "/cgi-bin/",\r
41244        expires: new Date(new Date().getTime()+(1000*60*60*24*30)), //30 days\r
41245        domain: "extjs.com"\r
41246    });\r
41247    Ext.state.Manager.setProvider(cp);\r
41248  </code></pre>\r
41249  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)\r
41250  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)\r
41251  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than\r
41252  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'extjs.com' to include\r
41253  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same\r
41254  * domain the page is running on including the 'www' like 'www.extjs.com')\r
41255  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)\r
41256  * @constructor\r
41257  * Create a new CookieProvider\r
41258  * @param {Object} config The configuration object\r
41259  */\r
41260 Ext.state.CookieProvider = function(config){\r
41261     Ext.state.CookieProvider.superclass.constructor.call(this);\r
41262     this.path = "/";\r
41263     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days\r
41264     this.domain = null;\r
41265     this.secure = false;\r
41266     Ext.apply(this, config);\r
41267     this.state = this.readCookies();\r
41268 };\r
41269 \r
41270 Ext.extend(Ext.state.CookieProvider, Ext.state.Provider, {\r
41271     // private\r
41272     set : function(name, value){\r
41273         if(typeof value == "undefined" || value === null){\r
41274             this.clear(name);\r
41275             return;\r
41276         }\r
41277         this.setCookie(name, value);\r
41278         Ext.state.CookieProvider.superclass.set.call(this, name, value);\r
41279     },\r
41280 \r
41281     // private\r
41282     clear : function(name){\r
41283         this.clearCookie(name);\r
41284         Ext.state.CookieProvider.superclass.clear.call(this, name);\r
41285     },\r
41286 \r
41287     // private\r
41288     readCookies : function(){\r
41289         var cookies = {};\r
41290         var c = document.cookie + ";";\r
41291         var re = /\s?(.*?)=(.*?);/g;\r
41292         var matches;\r
41293         while((matches = re.exec(c)) != null){\r
41294             var name = matches[1];\r
41295             var value = matches[2];\r
41296             if(name && name.substring(0,3) == "ys-"){\r
41297                 cookies[name.substr(3)] = this.decodeValue(value);\r
41298             }\r
41299         }\r
41300         return cookies;\r
41301     },\r
41302 \r
41303     // private\r
41304     setCookie : function(name, value){\r
41305         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +\r
41306            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +\r
41307            ((this.path == null) ? "" : ("; path=" + this.path)) +\r
41308            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +\r
41309            ((this.secure == true) ? "; secure" : "");\r
41310     },\r
41311 \r
41312     // private\r
41313     clearCookie : function(name){\r
41314         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +\r
41315            ((this.path == null) ? "" : ("; path=" + this.path)) +\r
41316            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +\r
41317            ((this.secure == true) ? "; secure" : "");\r
41318     }\r
41319 });/**
41320  * @class Ext.DataView
41321  * @extends Ext.BoxComponent
41322  * A mechanism for displaying data using custom layout templates and formatting. DataView uses an {@link Ext.XTemplate}
41323  * as its internal templating mechanism, and is bound to an {@link Ext.data.Store}
41324  * so that as the data in the store changes the view is automatically updated to reflect the changes.  The view also
41325  * provides built-in behavior for many common events that can occur for its contained items including click, doubleclick,
41326  * mouseover, mouseout, etc. as well as a built-in selection model. <b>In order to use these features, an {@link #itemSelector}
41327  * config must be provided for the DataView to determine what nodes it will be working with.</b>
41328  *
41329  * <p>The example below binds a DataView to a {@link Ext.data.Store} and renders it into an {@link Ext.Panel}.</p>
41330  * <pre><code>
41331 var store = new Ext.data.JsonStore({
41332     url: 'get-images.php',
41333     root: 'images',
41334     fields: [
41335         'name', 'url',
41336         {name:'size', type: 'float'},
41337         {name:'lastmod', type:'date', dateFormat:'timestamp'}
41338     ]
41339 });
41340 store.load();
41341
41342 var tpl = new Ext.XTemplate(
41343     '&lt;tpl for="."&gt;',
41344         '&lt;div class="thumb-wrap" id="{name}"&gt;',
41345         '&lt;div class="thumb"&gt;&lt;img src="{url}" title="{name}"&gt;&lt;/div&gt;',
41346         '&lt;span class="x-editable"&gt;{shortName}&lt;/span&gt;&lt;/div&gt;',
41347     '&lt;/tpl&gt;',
41348     '&lt;div class="x-clear"&gt;&lt;/div&gt;'
41349 );
41350
41351 var panel = new Ext.Panel({
41352     id:'images-view',
41353     frame:true,
41354     width:535,
41355     autoHeight:true,
41356     collapsible:true,
41357     layout:'fit',
41358     title:'Simple DataView',
41359
41360     items: new Ext.DataView({
41361         store: store,
41362         tpl: tpl,
41363         autoHeight:true,
41364         multiSelect: true,
41365         overClass:'x-view-over',
41366         itemSelector:'div.thumb-wrap',
41367         emptyText: 'No images to display'
41368     })
41369 });
41370 panel.render(document.body);
41371 </code></pre>
41372  * @constructor
41373  * Create a new DataView
41374  * @param {Object} config The config object
41375  * @xtype dataview
41376  */
41377 Ext.DataView = Ext.extend(Ext.BoxComponent, {
41378     /**
41379      * @cfg {String/Array} tpl
41380      * The HTML fragment or an array of fragments that will make up the template used by this DataView.  This should
41381      * be specified in the same format expected by the constructor of {@link Ext.XTemplate}.
41382      */
41383     /**
41384      * @cfg {Ext.data.Store} store
41385      * The {@link Ext.data.Store} to bind this DataView to.
41386      */
41387     /**
41388      * @cfg {String} itemSelector
41389      * <b>This is a required setting</b>. A simple CSS selector (e.g. <tt>div.some-class</tt> or 
41390      * <tt>span:first-child</tt>) that will be used to determine what nodes this DataView will be
41391      * working with.
41392      */
41393     /**
41394      * @cfg {Boolean} multiSelect
41395      * True to allow selection of more than one item at a time, false to allow selection of only a single item
41396      * at a time or no selection at all, depending on the value of {@link #singleSelect} (defaults to false).
41397      */
41398     /**
41399      * @cfg {Boolean} singleSelect
41400      * True to allow selection of exactly one item at a time, false to allow no selection at all (defaults to false).
41401      * Note that if {@link #multiSelect} = true, this value will be ignored.
41402      */
41403     /**
41404      * @cfg {Boolean} simpleSelect
41405      * True to enable multiselection by clicking on multiple items without requiring the user to hold Shift or Ctrl,
41406      * false to force the user to hold Ctrl or Shift to select more than on item (defaults to false).
41407      */
41408     /**
41409      * @cfg {String} overClass
41410      * A CSS class to apply to each item in the view on mouseover (defaults to undefined).
41411      */
41412     /**
41413      * @cfg {String} loadingText
41414      * A string to display during data load operations (defaults to undefined).  If specified, this text will be
41415      * displayed in a loading div and the view's contents will be cleared while loading, otherwise the view's
41416      * contents will continue to display normally until the new data is loaded and the contents are replaced.
41417      */
41418     /**
41419      * @cfg {String} selectedClass
41420      * A CSS class to apply to each selected item in the view (defaults to 'x-view-selected').
41421      */
41422     selectedClass : "x-view-selected",
41423     /**
41424      * @cfg {String} emptyText
41425      * The text to display in the view when there is no data to display (defaults to '').
41426      */
41427     emptyText : "",
41428
41429     /**
41430      * @cfg {Boolean} deferEmptyText True to defer emptyText being applied until the store's first load
41431      */
41432     deferEmptyText: true,
41433     /**
41434      * @cfg {Boolean} trackOver True to enable mouseenter and mouseleave events
41435      */
41436     trackOver: false,
41437
41438     //private
41439     last: false,
41440
41441     // private
41442     initComponent : function(){
41443         Ext.DataView.superclass.initComponent.call(this);
41444         if(Ext.isString(this.tpl) || Ext.isArray(this.tpl)){
41445             this.tpl = new Ext.XTemplate(this.tpl);
41446         }
41447
41448         this.addEvents(
41449             /**
41450              * @event beforeclick
41451              * Fires before a click is processed. Returns false to cancel the default action.
41452              * @param {Ext.DataView} this
41453              * @param {Number} index The index of the target node
41454              * @param {HTMLElement} node The target node
41455              * @param {Ext.EventObject} e The raw event object
41456              */
41457             "beforeclick",
41458             /**
41459              * @event click
41460              * Fires when a template node is clicked.
41461              * @param {Ext.DataView} this
41462              * @param {Number} index The index of the target node
41463              * @param {HTMLElement} node The target node
41464              * @param {Ext.EventObject} e The raw event object
41465              */
41466             "click",
41467             /**
41468              * @event mouseenter
41469              * Fires when the mouse enters a template node. trackOver:true or an overClass must be set to enable this event.
41470              * @param {Ext.DataView} this
41471              * @param {Number} index The index of the target node
41472              * @param {HTMLElement} node The target node
41473              * @param {Ext.EventObject} e The raw event object
41474              */
41475             "mouseenter",
41476             /**
41477              * @event mouseleave
41478              * Fires when the mouse leaves a template node. trackOver:true or an overClass must be set to enable this event.
41479              * @param {Ext.DataView} this
41480              * @param {Number} index The index of the target node
41481              * @param {HTMLElement} node The target node
41482              * @param {Ext.EventObject} e The raw event object
41483              */
41484             "mouseleave",
41485             /**
41486              * @event containerclick
41487              * Fires when a click occurs and it is not on a template node.
41488              * @param {Ext.DataView} this
41489              * @param {Ext.EventObject} e The raw event object
41490              */
41491             "containerclick",
41492             /**
41493              * @event dblclick
41494              * Fires when a template node is double clicked.
41495              * @param {Ext.DataView} this
41496              * @param {Number} index The index of the target node
41497              * @param {HTMLElement} node The target node
41498              * @param {Ext.EventObject} e The raw event object
41499              */
41500             "dblclick",
41501             /**
41502              * @event contextmenu
41503              * Fires when a template node is right clicked.
41504              * @param {Ext.DataView} this
41505              * @param {Number} index The index of the target node
41506              * @param {HTMLElement} node The target node
41507              * @param {Ext.EventObject} e The raw event object
41508              */
41509             "contextmenu",
41510             /**
41511              * @event containercontextmenu
41512              * Fires when a right click occurs that is not on a template node.
41513              * @param {Ext.DataView} this
41514              * @param {Ext.EventObject} e The raw event object
41515              */
41516             "containercontextmenu",
41517             /**
41518              * @event selectionchange
41519              * Fires when the selected nodes change.
41520              * @param {Ext.DataView} this
41521              * @param {Array} selections Array of the selected nodes
41522              */
41523             "selectionchange",
41524
41525             /**
41526              * @event beforeselect
41527              * Fires before a selection is made. If any handlers return false, the selection is cancelled.
41528              * @param {Ext.DataView} this
41529              * @param {HTMLElement} node The node to be selected
41530              * @param {Array} selections Array of currently selected nodes
41531              */
41532             "beforeselect"
41533         );
41534
41535         this.store = Ext.StoreMgr.lookup(this.store);
41536         this.all = new Ext.CompositeElementLite();
41537         this.selected = new Ext.CompositeElementLite();
41538     },
41539
41540     // private
41541     afterRender : function(){
41542         Ext.DataView.superclass.afterRender.call(this);
41543
41544                 this.mon(this.getTemplateTarget(), {
41545             "click": this.onClick,
41546             "dblclick": this.onDblClick,
41547             "contextmenu": this.onContextMenu,
41548             scope:this
41549         });
41550
41551         if(this.overClass || this.trackOver){
41552             this.mon(this.getTemplateTarget(), {
41553                 "mouseover": this.onMouseOver,
41554                 "mouseout": this.onMouseOut,
41555                 scope:this
41556             });
41557         }
41558
41559         if(this.store){
41560             this.bindStore(this.store, true);
41561         }
41562     },
41563
41564     /**
41565      * Refreshes the view by reloading the data from the store and re-rendering the template.
41566      */
41567     refresh : function(){
41568         this.clearSelections(false, true);
41569         var el = this.getTemplateTarget();
41570         el.update("");
41571         var records = this.store.getRange();
41572         if(records.length < 1){
41573             if(!this.deferEmptyText || this.hasSkippedEmptyText){
41574                 el.update(this.emptyText);
41575             }
41576             this.all.clear();
41577         }else{
41578             this.tpl.overwrite(el, this.collectData(records, 0));
41579             this.all.fill(Ext.query(this.itemSelector, el.dom));
41580             this.updateIndexes(0);
41581         }
41582         this.hasSkippedEmptyText = true;
41583     },
41584
41585     getTemplateTarget: function(){
41586         return this.el;
41587     },
41588
41589     /**
41590      * Function which can be overridden to provide custom formatting for each Record that is used by this
41591      * DataView's {@link #tpl template} to render each node.
41592      * @param {Array/Object} data The raw data object that was used to create the Record.
41593      * @param {Number} recordIndex the index number of the Record being prepared for rendering.
41594      * @param {Record} record The Record being prepared for rendering.
41595      * @return {Array/Object} The formatted data in a format expected by the internal {@link #tpl template}'s overwrite() method.
41596      * (either an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'}))
41597      */
41598     prepareData : function(data){
41599         return data;
41600     },
41601
41602     /**
41603      * <p>Function which can be overridden which returns the data object passed to this
41604      * DataView's {@link #tpl template} to render the whole DataView.</p>
41605      * <p>This is usually an Array of data objects, each element of which is processed by an
41606      * {@link Ext.XTemplate XTemplate} which uses <tt>'&lt;tpl for="."&gt;'</tt> to iterate over its supplied
41607      * data object as an Array. However, <i>named</i> properties may be placed into the data object to
41608      * provide non-repeating data such as headings, totals etc.</p>
41609      * @param {Array} records An Array of {@link Ext.data.Record}s to be rendered into the DataView.
41610      * @param {Number} startIndex the index number of the Record being prepared for rendering.
41611      * @return {Array} An Array of data objects to be processed by a repeating XTemplate. May also
41612      * contain <i>named</i> properties.
41613      */
41614     collectData : function(records, startIndex){
41615         var r = [];
41616         for(var i = 0, len = records.length; i < len; i++){
41617             r[r.length] = this.prepareData(records[i].data, startIndex+i, records[i]);
41618         }
41619         return r;
41620     },
41621
41622     // private
41623     bufferRender : function(records){
41624         var div = document.createElement('div');
41625         this.tpl.overwrite(div, this.collectData(records));
41626         return Ext.query(this.itemSelector, div);
41627     },
41628
41629     // private
41630     onUpdate : function(ds, record){
41631         var index = this.store.indexOf(record);
41632         if(index > -1){
41633             var sel = this.isSelected(index);
41634             var original = this.all.elements[index];
41635             var node = this.bufferRender([record], index)[0];
41636
41637             this.all.replaceElement(index, node, true);
41638             if(sel){
41639                 this.selected.replaceElement(original, node);
41640                 this.all.item(index).addClass(this.selectedClass);
41641             }
41642             this.updateIndexes(index, index);
41643         }
41644     },
41645
41646     // private
41647     onAdd : function(ds, records, index){
41648         if(this.all.getCount() === 0){
41649             this.refresh();
41650             return;
41651         }
41652         var nodes = this.bufferRender(records, index), n, a = this.all.elements;
41653         if(index < this.all.getCount()){
41654             n = this.all.item(index).insertSibling(nodes, 'before', true);
41655             a.splice.apply(a, [index, 0].concat(nodes));
41656         }else{
41657             n = this.all.last().insertSibling(nodes, 'after', true);
41658             a.push.apply(a, nodes);
41659         }
41660         this.updateIndexes(index);
41661     },
41662
41663     // private
41664     onRemove : function(ds, record, index){
41665         this.deselect(index);
41666         this.all.removeElement(index, true);
41667         this.updateIndexes(index);
41668         if (this.store.getCount() === 0){
41669             this.refresh();
41670         }
41671     },
41672
41673     /**
41674      * Refreshes an individual node's data from the store.
41675      * @param {Number} index The item's data index in the store
41676      */
41677     refreshNode : function(index){
41678         this.onUpdate(this.store, this.store.getAt(index));
41679     },
41680
41681     // private
41682     updateIndexes : function(startIndex, endIndex){
41683         var ns = this.all.elements;
41684         startIndex = startIndex || 0;
41685         endIndex = endIndex || ((endIndex === 0) ? 0 : (ns.length - 1));
41686         for(var i = startIndex; i <= endIndex; i++){
41687             ns[i].viewIndex = i;
41688         }
41689     },
41690     
41691     /**
41692      * Returns the store associated with this DataView.
41693      * @return {Ext.data.Store} The store
41694      */
41695     getStore : function(){
41696         return this.store;
41697     },
41698
41699     /**
41700      * Changes the data store bound to this view and refreshes it.
41701      * @param {Store} store The store to bind to this view
41702      */
41703     bindStore : function(store, initial){
41704         if(!initial && this.store){
41705             if(store !== this.store && this.store.autoDestroy){
41706                 this.store.destroy();
41707             }else{
41708                 this.store.un("beforeload", this.onBeforeLoad, this);
41709                 this.store.un("datachanged", this.refresh, this);
41710                 this.store.un("add", this.onAdd, this);
41711                 this.store.un("remove", this.onRemove, this);
41712                 this.store.un("update", this.onUpdate, this);
41713                 this.store.un("clear", this.refresh, this);
41714             }
41715             if(!store){
41716                 this.store = null;
41717             }
41718         }
41719         if(store){
41720             store = Ext.StoreMgr.lookup(store);
41721             store.on({
41722                 scope: this,
41723                 beforeload: this.onBeforeLoad,
41724                 datachanged: this.refresh,
41725                 add: this.onAdd,
41726                 remove: this.onRemove,
41727                 update: this.onUpdate,
41728                 clear: this.refresh
41729             });
41730         }
41731         this.store = store;
41732         if(store){
41733             this.refresh();
41734         }
41735     },
41736
41737     /**
41738      * Returns the template node the passed child belongs to, or null if it doesn't belong to one.
41739      * @param {HTMLElement} node
41740      * @return {HTMLElement} The template node
41741      */
41742     findItemFromChild : function(node){
41743         return Ext.fly(node).findParent(this.itemSelector, this.getTemplateTarget());
41744     },
41745
41746     // private
41747     onClick : function(e){
41748         var item = e.getTarget(this.itemSelector, this.getTemplateTarget());
41749         if(item){
41750             var index = this.indexOf(item);
41751             if(this.onItemClick(item, index, e) !== false){
41752                 this.fireEvent("click", this, index, item, e);
41753             }
41754         }else{
41755             if(this.fireEvent("containerclick", this, e) !== false){
41756                 this.onContainerClick(e);
41757             }
41758         }
41759     },
41760
41761     onContainerClick : function(e){
41762         this.clearSelections();
41763     },
41764
41765     // private
41766     onContextMenu : function(e){
41767         var item = e.getTarget(this.itemSelector, this.getTemplateTarget());
41768         if(item){
41769             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
41770         }else{
41771             this.fireEvent("containercontextmenu", this, e);
41772         }
41773     },
41774
41775     // private
41776     onDblClick : function(e){
41777         var item = e.getTarget(this.itemSelector, this.getTemplateTarget());
41778         if(item){
41779             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
41780         }
41781     },
41782
41783     // private
41784     onMouseOver : function(e){
41785         var item = e.getTarget(this.itemSelector, this.getTemplateTarget());
41786         if(item && item !== this.lastItem){
41787             this.lastItem = item;
41788             Ext.fly(item).addClass(this.overClass);
41789             this.fireEvent("mouseenter", this, this.indexOf(item), item, e);
41790         }
41791     },
41792
41793     // private
41794     onMouseOut : function(e){
41795         if(this.lastItem){
41796             if(!e.within(this.lastItem, true, true)){
41797                 Ext.fly(this.lastItem).removeClass(this.overClass);
41798                 this.fireEvent("mouseleave", this, this.indexOf(this.lastItem), this.lastItem, e);
41799                 delete this.lastItem;
41800             }
41801         }
41802     },
41803
41804     // private
41805     onItemClick : function(item, index, e){
41806         if(this.fireEvent("beforeclick", this, index, item, e) === false){
41807             return false;
41808         }
41809         if(this.multiSelect){
41810             this.doMultiSelection(item, index, e);
41811             e.preventDefault();
41812         }else if(this.singleSelect){
41813             this.doSingleSelection(item, index, e);
41814             e.preventDefault();
41815         }
41816         return true;
41817     },
41818
41819     // private
41820     doSingleSelection : function(item, index, e){
41821         if(e.ctrlKey && this.isSelected(index)){
41822             this.deselect(index);
41823         }else{
41824             this.select(index, false);
41825         }
41826     },
41827
41828     // private
41829     doMultiSelection : function(item, index, e){
41830         if(e.shiftKey && this.last !== false){
41831             var last = this.last;
41832             this.selectRange(last, index, e.ctrlKey);
41833             this.last = last; // reset the last
41834         }else{
41835             if((e.ctrlKey||this.simpleSelect) && this.isSelected(index)){
41836                 this.deselect(index);
41837             }else{
41838                 this.select(index, e.ctrlKey || e.shiftKey || this.simpleSelect);
41839             }
41840         }
41841     },
41842
41843     /**
41844      * Gets the number of selected nodes.
41845      * @return {Number} The node count
41846      */
41847     getSelectionCount : function(){
41848         return this.selected.getCount();
41849     },
41850
41851     /**
41852      * Gets the currently selected nodes.
41853      * @return {Array} An array of HTMLElements
41854      */
41855     getSelectedNodes : function(){
41856         return this.selected.elements;
41857     },
41858
41859     /**
41860      * Gets the indexes of the selected nodes.
41861      * @return {Array} An array of numeric indexes
41862      */
41863     getSelectedIndexes : function(){
41864         var indexes = [], s = this.selected.elements;
41865         for(var i = 0, len = s.length; i < len; i++){
41866             indexes.push(s[i].viewIndex);
41867         }
41868         return indexes;
41869     },
41870
41871     /**
41872      * Gets an array of the selected records
41873      * @return {Array} An array of {@link Ext.data.Record} objects
41874      */
41875     getSelectedRecords : function(){
41876         var r = [], s = this.selected.elements;
41877         for(var i = 0, len = s.length; i < len; i++){
41878             r[r.length] = this.store.getAt(s[i].viewIndex);
41879         }
41880         return r;
41881     },
41882
41883     /**
41884      * Gets an array of the records from an array of nodes
41885      * @param {Array} nodes The nodes to evaluate
41886      * @return {Array} records The {@link Ext.data.Record} objects
41887      */
41888     getRecords : function(nodes){
41889         var r = [], s = nodes;
41890         for(var i = 0, len = s.length; i < len; i++){
41891             r[r.length] = this.store.getAt(s[i].viewIndex);
41892         }
41893         return r;
41894     },
41895
41896     /**
41897      * Gets a record from a node
41898      * @param {HTMLElement} node The node to evaluate
41899      * @return {Record} record The {@link Ext.data.Record} object
41900      */
41901     getRecord : function(node){
41902         return this.store.getAt(node.viewIndex);
41903     },
41904
41905     /**
41906      * Clears all selections.
41907      * @param {Boolean} suppressEvent (optional) True to skip firing of the selectionchange event
41908      */
41909     clearSelections : function(suppressEvent, skipUpdate){
41910         if((this.multiSelect || this.singleSelect) && this.selected.getCount() > 0){
41911             if(!skipUpdate){
41912                 this.selected.removeClass(this.selectedClass);
41913             }
41914             this.selected.clear();
41915             this.last = false;
41916             if(!suppressEvent){
41917                 this.fireEvent("selectionchange", this, this.selected.elements);
41918             }
41919         }
41920     },
41921
41922     /**
41923      * Returns true if the passed node is selected, else false.
41924      * @param {HTMLElement/Number} node The node or node index to check
41925      * @return {Boolean} True if selected, else false
41926      */
41927     isSelected : function(node){
41928         return this.selected.contains(this.getNode(node));
41929     },
41930
41931     /**
41932      * Deselects a node.
41933      * @param {HTMLElement/Number} node The node to deselect
41934      */
41935     deselect : function(node){
41936         if(this.isSelected(node)){
41937             node = this.getNode(node);
41938             this.selected.removeElement(node);
41939             if(this.last == node.viewIndex){
41940                 this.last = false;
41941             }
41942             Ext.fly(node).removeClass(this.selectedClass);
41943             this.fireEvent("selectionchange", this, this.selected.elements);
41944         }
41945     },
41946
41947     /**
41948      * Selects a set of nodes.
41949      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node,
41950      * id of a template node or an array of any of those to select
41951      * @param {Boolean} keepExisting (optional) true to keep existing selections
41952      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
41953      */
41954     select : function(nodeInfo, keepExisting, suppressEvent){
41955         if(Ext.isArray(nodeInfo)){
41956             if(!keepExisting){
41957                 this.clearSelections(true);
41958             }
41959             for(var i = 0, len = nodeInfo.length; i < len; i++){
41960                 this.select(nodeInfo[i], true, true);
41961             }
41962             if(!suppressEvent){
41963                 this.fireEvent("selectionchange", this, this.selected.elements);
41964             }
41965         } else{
41966             var node = this.getNode(nodeInfo);
41967             if(!keepExisting){
41968                 this.clearSelections(true);
41969             }
41970             if(node && !this.isSelected(node)){
41971                 if(this.fireEvent("beforeselect", this, node, this.selected.elements) !== false){
41972                     Ext.fly(node).addClass(this.selectedClass);
41973                     this.selected.add(node);
41974                     this.last = node.viewIndex;
41975                     if(!suppressEvent){
41976                         this.fireEvent("selectionchange", this, this.selected.elements);
41977                     }
41978                 }
41979             }
41980         }
41981     },
41982
41983     /**
41984      * Selects a range of nodes. All nodes between start and end are selected.
41985      * @param {Number} start The index of the first node in the range
41986      * @param {Number} end The index of the last node in the range
41987      * @param {Boolean} keepExisting (optional) True to retain existing selections
41988      */
41989     selectRange : function(start, end, keepExisting){
41990         if(!keepExisting){
41991             this.clearSelections(true);
41992         }
41993         this.select(this.getNodes(start, end), true);
41994     },
41995
41996     /**
41997      * Gets a template node.
41998      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
41999      * @return {HTMLElement} The node or null if it wasn't found
42000      */
42001     getNode : function(nodeInfo){
42002         if(Ext.isString(nodeInfo)){
42003             return document.getElementById(nodeInfo);
42004         }else if(Ext.isNumber(nodeInfo)){
42005             return this.all.elements[nodeInfo];
42006         }
42007         return nodeInfo;
42008     },
42009
42010     /**
42011      * Gets a range nodes.
42012      * @param {Number} start (optional) The index of the first node in the range
42013      * @param {Number} end (optional) The index of the last node in the range
42014      * @return {Array} An array of nodes
42015      */
42016     getNodes : function(start, end){
42017         var ns = this.all.elements;
42018         start = start || 0;
42019         end = !Ext.isDefined(end) ? Math.max(ns.length - 1, 0) : end;
42020         var nodes = [], i;
42021         if(start <= end){
42022             for(i = start; i <= end && ns[i]; i++){
42023                 nodes.push(ns[i]);
42024             }
42025         } else{
42026             for(i = start; i >= end && ns[i]; i--){
42027                 nodes.push(ns[i]);
42028             }
42029         }
42030         return nodes;
42031     },
42032
42033     /**
42034      * Finds the index of the passed node.
42035      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
42036      * @return {Number} The index of the node or -1
42037      */
42038     indexOf : function(node){
42039         node = this.getNode(node);
42040         if(Ext.isNumber(node.viewIndex)){
42041             return node.viewIndex;
42042         }
42043         return this.all.indexOf(node);
42044     },
42045
42046     // private
42047     onBeforeLoad : function(){
42048         if(this.loadingText){
42049             this.clearSelections(false, true);
42050             this.getTemplateTarget().update('<div class="loading-indicator">'+this.loadingText+'</div>');
42051             this.all.clear();
42052         }
42053     },
42054
42055     onDestroy : function(){
42056         this.all.clear();
42057         this.selected.clear();
42058         Ext.DataView.superclass.onDestroy.call(this);
42059         this.bindStore(null);
42060     }
42061 });
42062
42063 /**
42064  * Changes the data store bound to this view and refreshes it. (deprecated in favor of bindStore)
42065  * @param {Store} store The store to bind to this view
42066  */
42067 Ext.DataView.prototype.setStore = Ext.DataView.prototype.bindStore;
42068
42069 Ext.reg('dataview', Ext.DataView);
42070 /**\r
42071  * @class Ext.list.ListView\r
42072  * @extends Ext.DataView\r
42073  * <p>Ext.list.ListView is a fast and light-weight implentation of a\r
42074  * {@link Ext.grid.GridPanel Grid} like view with the following characteristics:</p>\r
42075  * <div class="mdetail-params"><ul>\r
42076  * <li>resizable columns</li>\r
42077  * <li>selectable</li>\r
42078  * <li>column widths are initially proportioned by percentage based on the container\r
42079  * width and number of columns</li>\r
42080  * <li>uses templates to render the data in any required format</li>\r
42081  * <li>no horizontal scrolling</li>\r
42082  * <li>no editing</li>\r
42083  * </ul></div>\r
42084  * <p>Example usage:</p>\r
42085  * <pre><code>\r
42086 // consume JSON of this form:\r
42087 {\r
42088    "images":[\r
42089       {\r
42090          "name":"dance_fever.jpg",\r
42091          "size":2067,\r
42092          "lastmod":1236974993000,\r
42093          "url":"images\/thumbs\/dance_fever.jpg"\r
42094       },\r
42095       {\r
42096          "name":"zack_sink.jpg",\r
42097          "size":2303,\r
42098          "lastmod":1236974993000,\r
42099          "url":"images\/thumbs\/zack_sink.jpg"\r
42100       }\r
42101    ]\r
42102\r
42103 var store = new Ext.data.JsonStore({\r
42104     url: 'get-images.php',\r
42105     root: 'images',\r
42106     fields: [\r
42107         'name', 'url',\r
42108         {name:'size', type: 'float'},\r
42109         {name:'lastmod', type:'date', dateFormat:'timestamp'}\r
42110     ]\r
42111 });\r
42112 store.load();\r
42113 \r
42114 var listView = new Ext.list.ListView({\r
42115     store: store,\r
42116     multiSelect: true,\r
42117     emptyText: 'No images to display',\r
42118     reserveScrollOffset: true,\r
42119     columns: [{\r
42120         header: 'File',\r
42121         width: .5,\r
42122         dataIndex: 'name'\r
42123     },{\r
42124         header: 'Last Modified',\r
42125         width: .35, \r
42126         dataIndex: 'lastmod',\r
42127         tpl: '{lastmod:date("m-d h:i a")}'\r
42128     },{\r
42129         header: 'Size',\r
42130         dataIndex: 'size',\r
42131         tpl: '{size:fileSize}', // format using Ext.util.Format.fileSize()\r
42132         align: 'right'\r
42133     }]\r
42134 });\r
42135 \r
42136 // put it in a Panel so it looks pretty\r
42137 var panel = new Ext.Panel({\r
42138     id:'images-view',\r
42139     width:425,\r
42140     height:250,\r
42141     collapsible:true,\r
42142     layout:'fit',\r
42143     title:'Simple ListView <i>(0 items selected)</i>',\r
42144     items: listView\r
42145 });\r
42146 panel.render(document.body);\r
42147 \r
42148 // little bit of feedback\r
42149 listView.on('selectionchange', function(view, nodes){\r
42150     var l = nodes.length;\r
42151     var s = l != 1 ? 's' : '';\r
42152     panel.setTitle('Simple ListView <i>('+l+' item'+s+' selected)</i>');\r
42153 });\r
42154  * </code></pre>\r
42155  * @constructor\r
42156  * @param {Object} config\r
42157  * @xtype listview\r
42158  */\r
42159 Ext.list.ListView = Ext.extend(Ext.DataView, {\r
42160     /**\r
42161      * Set this property to <tt>true</tt> to disable the header click handler disabling sort\r
42162      * (defaults to <tt>false</tt>).\r
42163      * @type Boolean\r
42164      * @property disableHeaders\r
42165      */\r
42166     /**\r
42167      * @cfg {Boolean} hideHeaders\r
42168      * <tt>true</tt> to hide the {@link #internalTpl header row} (defaults to <tt>false</tt> so\r
42169      * the {@link #internalTpl header row} will be shown).\r
42170      */\r
42171     /**\r
42172      * @cfg {String} itemSelector\r
42173      * Defaults to <tt>'dl'</tt> to work with the preconfigured <b><tt>{@link Ext.DataView#tpl tpl}</tt></b>.\r
42174      * This setting specifies the CSS selector (e.g. <tt>div.some-class</tt> or <tt>span:first-child</tt>)\r
42175      * that will be used to determine what nodes the ListView will be working with.   \r
42176      */\r
42177     itemSelector: 'dl',\r
42178     /**\r
42179      * @cfg {String} selectedClass The CSS class applied to a selected row (defaults to\r
42180      * <tt>'x-list-selected'</tt>). An example overriding the default styling:\r
42181     <pre><code>\r
42182     .x-list-selected {background-color: yellow;}\r
42183     </code></pre>\r
42184      * @type String\r
42185      */\r
42186     selectedClass:'x-list-selected',\r
42187     /**\r
42188      * @cfg {String} overClass The CSS class applied when over a row (defaults to\r
42189      * <tt>'x-list-over'</tt>). An example overriding the default styling:\r
42190     <pre><code>\r
42191     .x-list-over {background-color: orange;}\r
42192     </code></pre>\r
42193      * @type String\r
42194      */\r
42195     overClass:'x-list-over',\r
42196     /**\r
42197      * @cfg {Boolean} reserveScrollOffset\r
42198      * By default will defer accounting for the configured <b><tt>{@link #scrollOffset}</tt></b>\r
42199      * for 10 milliseconds.  Specify <tt>true</tt> to account for the configured\r
42200      * <b><tt>{@link #scrollOffset}</tt></b> immediately.\r
42201      */\r
42202     /**\r
42203      * @cfg {Number} scrollOffset The amount of space to reserve for the scrollbar (defaults to\r
42204      * <tt>undefined</tt>). If an explicit value isn't specified, this will be automatically\r
42205      * calculated.\r
42206      */\r
42207     scrollOffset : undefined,\r
42208     /**\r
42209      * @cfg {Boolean/Object} columnResize\r
42210      * Specify <tt>true</tt> or specify a configuration object for {@link Ext.list.ListView.ColumnResizer}\r
42211      * to enable the columns to be resizable (defaults to <tt>true</tt>).\r
42212      */\r
42213     columnResize: true,\r
42214     /**\r
42215      * @cfg {Array} columns An array of column configuration objects, for example:\r
42216      * <pre><code>\r
42217 {\r
42218     align: 'right',\r
42219     dataIndex: 'size',\r
42220     header: 'Size',\r
42221     tpl: '{size:fileSize}',\r
42222     width: .35\r
42223 }\r
42224      * </code></pre> \r
42225      * Acceptable properties for each column configuration object are:\r
42226      * <div class="mdetail-params"><ul>\r
42227      * <li><b><tt>align</tt></b> : String<div class="sub-desc">Set the CSS text-align property\r
42228      * of the column. Defaults to <tt>'left'</tt>.</div></li>\r
42229      * <li><b><tt>dataIndex</tt></b> : String<div class="sub-desc">See {@link Ext.grid.Column}.\r
42230      * {@link Ext.grid.Column#dataIndex dataIndex} for details.</div></li>\r
42231      * <li><b><tt>header</tt></b> : String<div class="sub-desc">See {@link Ext.grid.Column}.\r
42232      * {@link Ext.grid.Column#header header} for details.</div></li>\r
42233      * <li><b><tt>tpl</tt></b> : String<div class="sub-desc">Specify a string to pass as the\r
42234      * configuration string for {@link Ext.XTemplate}.  By default an {@link Ext.XTemplate}\r
42235      * will be implicitly created using the <tt>dataIndex</tt>.</div></li>\r
42236      * <li><b><tt>width</tt></b> : Number<div class="sub-desc">Percentage of the container width\r
42237      * this column should be allocated.  Columns that have no width specified will be\r
42238      * allocated with an equal percentage to fill 100% of the container width.  To easily take\r
42239      * advantage of the full container width, leave the width of at least one column undefined.\r
42240      * Note that if you do not want to take up the full width of the container, the width of\r
42241      * every column needs to be explicitly defined.</div></li>\r
42242      * </ul></div>\r
42243      */\r
42244     /**\r
42245      * @cfg {Boolean/Object} columnSort\r
42246      * Specify <tt>true</tt> or specify a configuration object for {@link Ext.list.ListView.Sorter}\r
42247      * to enable the columns to be sortable (defaults to <tt>true</tt>).\r
42248      */\r
42249     columnSort: true,\r
42250     /**\r
42251      * @cfg {String/Array} internalTpl\r
42252      * The template to be used for the header row.  See {@link #tpl} for more details.\r
42253      */\r
42254 \r
42255     /*\r
42256      * IE has issues when setting percentage based widths to 100%. Default to 99.\r
42257      */\r
42258     maxWidth: Ext.isIE ? 99 : 100,\r
42259     \r
42260     initComponent : function(){\r
42261         if(this.columnResize){\r
42262             this.colResizer = new Ext.list.ColumnResizer(this.colResizer);\r
42263             this.colResizer.init(this);\r
42264         }\r
42265         if(this.columnSort){\r
42266             this.colSorter = new Ext.list.Sorter(this.columnSort);\r
42267             this.colSorter.init(this);\r
42268         }\r
42269         if(!this.internalTpl){\r
42270             this.internalTpl = new Ext.XTemplate(\r
42271                 '<div class="x-list-header"><div class="x-list-header-inner">',\r
42272                     '<tpl for="columns">',\r
42273                     '<div style="width:{[values.width*100]}%;text-align:{align};"><em unselectable="on" id="',this.id, '-xlhd-{#}">',\r
42274                         '{header}',\r
42275                     '</em></div>',\r
42276                     '</tpl>',\r
42277                     '<div class="x-clear"></div>',\r
42278                 '</div></div>',\r
42279                 '<div class="x-list-body"><div class="x-list-body-inner">',\r
42280                 '</div></div>'\r
42281             );\r
42282         }\r
42283         if(!this.tpl){\r
42284             this.tpl = new Ext.XTemplate(\r
42285                 '<tpl for="rows">',\r
42286                     '<dl>',\r
42287                         '<tpl for="parent.columns">',\r
42288                         '<dt style="width:{[values.width*100]}%;text-align:{align};">',\r
42289                         '<em unselectable="on"<tpl if="cls"> class="{cls}</tpl>">',\r
42290                             '{[values.tpl.apply(parent)]}',\r
42291                         '</em></dt>',\r
42292                         '</tpl>',\r
42293                         '<div class="x-clear"></div>',\r
42294                     '</dl>',\r
42295                 '</tpl>'\r
42296             );\r
42297         };\r
42298         \r
42299         var cs = this.columns, \r
42300             allocatedWidth = 0, \r
42301             colsWithWidth = 0, \r
42302             len = cs.length, \r
42303             columns = [];\r
42304             \r
42305         for(var i = 0; i < len; i++){\r
42306             var c = cs[i];\r
42307             if(!c.isColumn) {\r
42308                 c.xtype = c.xtype ? (/^lv/.test(c.xtype) ? c.xtype : 'lv' + c.xtype) : 'lvcolumn';\r
42309                 c = Ext.create(c);\r
42310             }\r
42311             if(c.width) {\r
42312                 allocatedWidth += c.width*100;\r
42313                 colsWithWidth++;\r
42314             }\r
42315             columns.push(c);\r
42316         }\r
42317         \r
42318         cs = this.columns = columns;\r
42319         \r
42320         // auto calculate missing column widths\r
42321         if(colsWithWidth < len){\r
42322             var remaining = len - colsWithWidth;\r
42323             if(allocatedWidth < this.maxWidth){\r
42324                 var perCol = ((this.maxWidth-allocatedWidth) / remaining)/100;\r
42325                 for(var j = 0; j < len; j++){\r
42326                     var c = cs[j];\r
42327                     if(!c.width){\r
42328                         c.width = perCol;\r
42329                     }\r
42330                 }\r
42331             }\r
42332         }\r
42333         Ext.list.ListView.superclass.initComponent.call(this);\r
42334     },\r
42335 \r
42336     onRender : function(){\r
42337         this.autoEl = {\r
42338             cls: 'x-list-wrap'  \r
42339         };\r
42340         Ext.list.ListView.superclass.onRender.apply(this, arguments);\r
42341 \r
42342         this.internalTpl.overwrite(this.el, {columns: this.columns});\r
42343         \r
42344         this.innerBody = Ext.get(this.el.dom.childNodes[1].firstChild);\r
42345         this.innerHd = Ext.get(this.el.dom.firstChild.firstChild);\r
42346 \r
42347         if(this.hideHeaders){\r
42348             this.el.dom.firstChild.style.display = 'none';\r
42349         }\r
42350     },\r
42351 \r
42352     getTemplateTarget : function(){\r
42353         return this.innerBody;\r
42354     },\r
42355 \r
42356     /**\r
42357      * <p>Function which can be overridden which returns the data object passed to this\r
42358      * view's {@link #tpl template} to render the whole ListView. The returned object \r
42359      * shall contain the following properties:</p>\r
42360      * <div class="mdetail-params"><ul>\r
42361      * <li><b>columns</b> : String<div class="sub-desc">See <tt>{@link #columns}</tt></div></li>\r
42362      * <li><b>rows</b> : String<div class="sub-desc">See\r
42363      * <tt>{@link Ext.DataView}.{@link Ext.DataView#collectData collectData}</div></li>\r
42364      * </ul></div>\r
42365      * @param {Array} records An Array of {@link Ext.data.Record}s to be rendered into the DataView.\r
42366      * @param {Number} startIndex the index number of the Record being prepared for rendering.\r
42367      * @return {Object} A data object containing properties to be processed by a repeating\r
42368      * XTemplate as described above.\r
42369      */\r
42370     collectData : function(){\r
42371         var rs = Ext.list.ListView.superclass.collectData.apply(this, arguments);\r
42372         return {\r
42373             columns: this.columns,\r
42374             rows: rs\r
42375         }\r
42376     },\r
42377 \r
42378     verifyInternalSize : function(){\r
42379         if(this.lastSize){\r
42380             this.onResize(this.lastSize.width, this.lastSize.height);\r
42381         }\r
42382     },\r
42383 \r
42384     // private\r
42385     onResize : function(w, h){\r
42386         var bd = this.innerBody.dom;\r
42387         var hd = this.innerHd.dom\r
42388         if(!bd){\r
42389             return;\r
42390         }\r
42391         var bdp = bd.parentNode;\r
42392         if(Ext.isNumber(w)){\r
42393             var sw = w - Ext.num(this.scrollOffset, Ext.getScrollBarWidth());\r
42394             if(this.reserveScrollOffset || ((bdp.offsetWidth - bdp.clientWidth) > 10)){\r
42395                 bd.style.width = sw + 'px';\r
42396                 hd.style.width = sw + 'px';\r
42397             }else{\r
42398                 bd.style.width = w + 'px';\r
42399                 hd.style.width = w + 'px';\r
42400                 setTimeout(function(){\r
42401                     if((bdp.offsetWidth - bdp.clientWidth) > 10){\r
42402                         bd.style.width = sw + 'px';\r
42403                         hd.style.width = sw + 'px';\r
42404                     }\r
42405                 }, 10);\r
42406             }\r
42407         }\r
42408         if(Ext.isNumber(h)){\r
42409             bdp.style.height = (h - hd.parentNode.offsetHeight) + 'px';\r
42410         }\r
42411     },\r
42412 \r
42413     updateIndexes : function(){\r
42414         Ext.list.ListView.superclass.updateIndexes.apply(this, arguments);\r
42415         this.verifyInternalSize();\r
42416     },\r
42417 \r
42418     findHeaderIndex : function(hd){\r
42419         hd = hd.dom || hd;\r
42420         var pn = hd.parentNode, cs = pn.parentNode.childNodes;\r
42421         for(var i = 0, c; c = cs[i]; i++){\r
42422             if(c == pn){\r
42423                 return i;\r
42424             }\r
42425         }\r
42426         return -1;\r
42427     },\r
42428 \r
42429     setHdWidths : function(){\r
42430         var els = this.innerHd.dom.getElementsByTagName('div');\r
42431         for(var i = 0, cs = this.columns, len = cs.length; i < len; i++){\r
42432             els[i].style.width = (cs[i].width*100) + '%';\r
42433         }\r
42434     }\r
42435 });\r
42436 \r
42437 Ext.reg('listview', Ext.list.ListView);\r
42438 \r
42439 // Backwards compatibility alias\r
42440 Ext.ListView = Ext.list.ListView;/**
42441  * @class Ext.list.Column
42442  * <p>This class encapsulates column configuration data to be used in the initialization of a
42443  * {@link Ext.list.ListView ListView}.</p>
42444  * <p>While subclasses are provided to render data in different ways, this class renders a passed
42445  * data field unchanged and is usually used for textual columns.</p>
42446  */
42447 Ext.list.Column = Ext.extend(Object, {
42448     /**
42449      * @private
42450      * @cfg {Boolean} isColumn
42451      * Used by ListView constructor method to avoid reprocessing a Column
42452      * if <code>isColumn</code> is not set ListView will recreate a new Ext.list.Column
42453      * Defaults to true.
42454      */
42455     isColumn: true,
42456     
42457     /**
42458      * @cfg {String} align
42459      * Set the CSS text-align property of the column. Defaults to <tt>'left'</tt>.
42460      */        
42461     align: 'left',
42462     /**
42463      * @cfg {String} header Optional. The header text to be used as innerHTML
42464      * (html tags are accepted) to display in the ListView.  <b>Note</b>: to
42465      * have a clickable header with no text displayed use <tt>'&#160;'</tt>.
42466      */    
42467     header: '',
42468     
42469     /**
42470      * @cfg {Number} width Optional. Percentage of the container width
42471      * this column should be allocated.  Columns that have no width specified will be
42472      * allocated with an equal percentage to fill 100% of the container width.  To easily take
42473      * advantage of the full container width, leave the width of at least one column undefined.
42474      * Note that if you do not want to take up the full width of the container, the width of
42475      * every column needs to be explicitly defined.
42476      */    
42477     width: null,
42478
42479     /**
42480      * @cfg {String} cls Optional. This option can be used to add a CSS class to the cell of each
42481      * row for this column.
42482      */
42483     cls: '',
42484     
42485     /**
42486      * @cfg {String} tpl Optional. Specify a string to pass as the
42487      * configuration string for {@link Ext.XTemplate}.  By default an {@link Ext.XTemplate}
42488      * will be implicitly created using the <tt>dataIndex</tt>.
42489      */
42490
42491     /**
42492      * @cfg {String} dataIndex <p><b>Required</b>. The name of the field in the
42493      * ListViews's {@link Ext.data.Store}'s {@link Ext.data.Record} definition from
42494      * which to draw the column's value.</p>
42495      */
42496     
42497     constructor : function(c){
42498         if(!c.tpl){
42499             c.tpl = new Ext.XTemplate('{' + c.dataIndex + '}');
42500         }
42501         else if(Ext.isString(c.tpl)){
42502             c.tpl = new Ext.XTemplate(c.tpl);
42503         }
42504         
42505         Ext.apply(this, c);
42506     }
42507 });
42508
42509 Ext.reg('lvcolumn', Ext.list.Column);
42510
42511 /**
42512  * @class Ext.list.NumberColumn
42513  * @extends Ext.list.Column
42514  * <p>A Column definition class which renders a numeric data field according to a {@link #format} string.  See the
42515  * {@link Ext.list.Column#xtype xtype} config option of {@link Ext.list.Column} for more details.</p>
42516  */
42517 Ext.list.NumberColumn = Ext.extend(Ext.list.Column, {
42518     /**
42519      * @cfg {String} format
42520      * A formatting string as used by {@link Ext.util.Format#number} to format a numeric value for this Column
42521      * (defaults to <tt>'0,000.00'</tt>).
42522      */    
42523     format: '0,000.00',
42524     
42525     constructor : function(c) {
42526         c.tpl = c.tpl || new Ext.XTemplate('{' + c.dataIndex + ':number("' + (c.format || this.format) + '")}');       
42527         Ext.list.NumberColumn.superclass.constructor.call(this, c);
42528     }
42529 });
42530
42531 Ext.reg('lvnumbercolumn', Ext.list.NumberColumn);
42532
42533 /**
42534  * @class Ext.list.DateColumn
42535  * @extends Ext.list.Column
42536  * <p>A Column definition class which renders a passed date according to the default locale, or a configured
42537  * {@link #format}. See the {@link Ext.list.Column#xtype xtype} config option of {@link Ext.list.Column}
42538  * for more details.</p>
42539  */
42540 Ext.list.DateColumn = Ext.extend(Ext.list.Column, {
42541     format: 'm/d/Y',
42542     constructor : function(c) {
42543         c.tpl = c.tpl || new Ext.XTemplate('{' + c.dataIndex + ':date("' + (c.format || this.format) + '")}');      
42544         Ext.list.DateColumn.superclass.constructor.call(this, c);
42545     }
42546 });
42547 Ext.reg('lvdatecolumn', Ext.list.DateColumn);
42548
42549 /**
42550  * @class Ext.list.BooleanColumn
42551  * @extends Ext.list.Column
42552  * <p>A Column definition class which renders boolean data fields.  See the {@link Ext.list.Column#xtype xtype}
42553  * config option of {@link Ext.list.Column} for more details.</p>
42554  */
42555 Ext.list.BooleanColumn = Ext.extend(Ext.list.Column, {
42556     /**
42557      * @cfg {String} trueText
42558      * The string returned by the renderer when the column value is not falsey (defaults to <tt>'true'</tt>).
42559      */
42560     trueText: 'true',
42561     /**
42562      * @cfg {String} falseText
42563      * The string returned by the renderer when the column value is falsey (but not undefined) (defaults to
42564      * <tt>'false'</tt>).
42565      */
42566     falseText: 'false',
42567     /**
42568      * @cfg {String} undefinedText
42569      * The string returned by the renderer when the column value is undefined (defaults to <tt>'&#160;'</tt>).
42570      */
42571     undefinedText: '&#160;',
42572     
42573     constructor : function(c) {
42574         c.tpl = c.tpl || new Ext.XTemplate('{' + c.dataIndex + ':this.format}');
42575         
42576         var t = this.trueText, f = this.falseText, u = this.undefinedText;
42577         c.tpl.format = function(v){
42578             if(v === undefined){
42579                 return u;
42580             }
42581             if(!v || v === 'false'){
42582                 return f;
42583             }
42584             return t;
42585         };
42586         
42587         Ext.list.DateColumn.superclass.constructor.call(this, c);
42588     }
42589 });
42590
42591 Ext.reg('lvbooleancolumn', Ext.list.BooleanColumn);/**\r
42592  * @class Ext.list.ColumnResizer\r
42593  * @extends Ext.util.Observable\r
42594  * <p>Supporting Class for Ext.list.ListView</p>\r
42595  * @constructor\r
42596  * @param {Object} config\r
42597  */\r
42598 Ext.list.ColumnResizer = Ext.extend(Ext.util.Observable, {\r
42599     /**\r
42600      * @cfg {Number} minPct The minimum percentage to allot for any column (defaults to <tt>.05</tt>)\r
42601      */\r
42602     minPct: .05,\r
42603 \r
42604     constructor: function(config){\r
42605         Ext.apply(this, config);\r
42606         Ext.list.ColumnResizer.superclass.constructor.call(this);\r
42607     },\r
42608     init : function(listView){\r
42609         this.view = listView;\r
42610         listView.on('render', this.initEvents, this);\r
42611     },\r
42612 \r
42613     initEvents : function(view){\r
42614         view.mon(view.innerHd, 'mousemove', this.handleHdMove, this);\r
42615         this.tracker = new Ext.dd.DragTracker({\r
42616             onBeforeStart: this.onBeforeStart.createDelegate(this),\r
42617             onStart: this.onStart.createDelegate(this),\r
42618             onDrag: this.onDrag.createDelegate(this),\r
42619             onEnd: this.onEnd.createDelegate(this),\r
42620             tolerance: 3,\r
42621             autoStart: 300\r
42622         });\r
42623         this.tracker.initEl(view.innerHd);\r
42624         view.on('beforedestroy', this.tracker.destroy, this.tracker);\r
42625     },\r
42626 \r
42627     handleHdMove : function(e, t){\r
42628         var hw = 5,\r
42629             x = e.getPageX(),\r
42630             hd = e.getTarget('em', 3, true);\r
42631         if(hd){\r
42632             var r = hd.getRegion(),\r
42633                 ss = hd.dom.style,\r
42634                 pn = hd.dom.parentNode;\r
42635 \r
42636             if(x - r.left <= hw && pn != pn.parentNode.firstChild){\r
42637                 this.activeHd = Ext.get(pn.previousSibling.firstChild);\r
42638                 ss.cursor = Ext.isWebKit ? 'e-resize' : 'col-resize';\r
42639             } else if(r.right - x <= hw && pn != pn.parentNode.lastChild.previousSibling){\r
42640                 this.activeHd = hd;\r
42641                 ss.cursor = Ext.isWebKit ? 'w-resize' : 'col-resize';\r
42642             } else{\r
42643                 delete this.activeHd;\r
42644                 ss.cursor = '';\r
42645             }\r
42646         }\r
42647     },\r
42648 \r
42649     onBeforeStart : function(e){\r
42650         this.dragHd = this.activeHd;\r
42651         return !!this.dragHd;\r
42652     },\r
42653 \r
42654     onStart: function(e){\r
42655         this.view.disableHeaders = true;\r
42656         this.proxy = this.view.el.createChild({cls:'x-list-resizer'});\r
42657         this.proxy.setHeight(this.view.el.getHeight());\r
42658 \r
42659         var x = this.tracker.getXY()[0],\r
42660             w = this.view.innerHd.getWidth();\r
42661 \r
42662         this.hdX = this.dragHd.getX();\r
42663         this.hdIndex = this.view.findHeaderIndex(this.dragHd);\r
42664 \r
42665         this.proxy.setX(this.hdX);\r
42666         this.proxy.setWidth(x-this.hdX);\r
42667 \r
42668         this.minWidth = w*this.minPct;\r
42669         this.maxWidth = w - (this.minWidth*(this.view.columns.length-1-this.hdIndex));\r
42670     },\r
42671 \r
42672     onDrag: function(e){\r
42673         var cursorX = this.tracker.getXY()[0];\r
42674         this.proxy.setWidth((cursorX-this.hdX).constrain(this.minWidth, this.maxWidth));\r
42675     },\r
42676 \r
42677     onEnd: function(e){\r
42678         /* calculate desired width by measuring proxy and then remove it */\r
42679         var nw = this.proxy.getWidth();\r
42680         this.proxy.remove();\r
42681 \r
42682         var index = this.hdIndex,\r
42683             vw = this.view,\r
42684             cs = vw.columns,\r
42685             len = cs.length,\r
42686             w = this.view.innerHd.getWidth(),\r
42687             minPct = this.minPct * 100,\r
42688             pct = Math.ceil((nw * vw.maxWidth) / w),\r
42689             diff = (cs[index].width * 100) - pct,\r
42690             each = Math.floor(diff / (len-1-index)),\r
42691             mod = diff - (each * (len-1-index));\r
42692 \r
42693         for(var i = index+1; i < len; i++){\r
42694             var cw = (cs[i].width * 100) + each,\r
42695                 ncw = Math.max(minPct, cw);\r
42696             if(cw != ncw){\r
42697                 mod += cw - ncw;\r
42698             }\r
42699             cs[i].width = ncw / 100;\r
42700         }\r
42701         cs[index].width = pct / 100;\r
42702         cs[index+1].width += (mod / 100);\r
42703         delete this.dragHd;\r
42704         vw.setHdWidths();\r
42705         vw.refresh();\r
42706         setTimeout(function(){\r
42707             vw.disableHeaders = false;\r
42708         }, 100);\r
42709     }\r
42710 });\r
42711 \r
42712 // Backwards compatibility alias\r
42713 Ext.ListView.ColumnResizer = Ext.list.ColumnResizer;/**\r
42714  * @class Ext.list.Sorter\r
42715  * @extends Ext.util.Observable\r
42716  * <p>Supporting Class for Ext.list.ListView</p>\r
42717  * @constructor\r
42718  * @param {Object} config\r
42719  */\r
42720 Ext.list.Sorter = Ext.extend(Ext.util.Observable, {\r
42721     /**\r
42722      * @cfg {Array} sortClasses\r
42723      * The CSS classes applied to a header when it is sorted. (defaults to <tt>["sort-asc", "sort-desc"]</tt>)\r
42724      */\r
42725     sortClasses : ["sort-asc", "sort-desc"],\r
42726 \r
42727     constructor: function(config){\r
42728         Ext.apply(this, config);\r
42729         Ext.list.Sorter.superclass.constructor.call(this);\r
42730     },\r
42731 \r
42732     init : function(listView){\r
42733         this.view = listView;\r
42734         listView.on('render', this.initEvents, this);\r
42735     },\r
42736 \r
42737     initEvents : function(view){\r
42738         view.mon(view.innerHd, 'click', this.onHdClick, this);\r
42739         view.innerHd.setStyle('cursor', 'pointer');\r
42740         view.mon(view.store, 'datachanged', this.updateSortState, this);\r
42741         this.updateSortState.defer(10, this, [view.store]);\r
42742     },\r
42743 \r
42744     updateSortState : function(store){\r
42745         var state = store.getSortState();\r
42746         if(!state){\r
42747             return;\r
42748         }\r
42749         this.sortState = state;\r
42750         var cs = this.view.columns, sortColumn = -1;\r
42751         for(var i = 0, len = cs.length; i < len; i++){\r
42752             if(cs[i].dataIndex == state.field){\r
42753                 sortColumn = i;\r
42754                 break;\r
42755             }\r
42756         }\r
42757         if(sortColumn != -1){\r
42758             var sortDir = state.direction;\r
42759             this.updateSortIcon(sortColumn, sortDir);\r
42760         }\r
42761     },\r
42762 \r
42763     updateSortIcon : function(col, dir){\r
42764         var sc = this.sortClasses;\r
42765         var hds = this.view.innerHd.select('em').removeClass(sc);\r
42766         hds.item(col).addClass(sc[dir == "DESC" ? 1 : 0]);\r
42767     },\r
42768 \r
42769     onHdClick : function(e){\r
42770         var hd = e.getTarget('em', 3);\r
42771         if(hd && !this.view.disableHeaders){\r
42772             var index = this.view.findHeaderIndex(hd);\r
42773             this.view.store.sort(this.view.columns[index].dataIndex);\r
42774         }\r
42775     }\r
42776 });\r
42777 \r
42778 // Backwards compatibility alias\r
42779 Ext.ListView.Sorter = Ext.list.Sorter;/**
42780  * @class Ext.TabPanel
42781  * <p>A basic tab container. TabPanels can be used exactly like a standard {@link Ext.Panel}
42782  * for layout purposes, but also have special support for containing child Components
42783  * (<tt>{@link Ext.Container#items items}</tt>) that are managed using a
42784  * {@link Ext.layout.CardLayout CardLayout layout manager}, and displayed as separate tabs.</p>
42785  *
42786  * <b>Note:</b> By default, a tab's close tool <i>destroys</i> the child tab Component
42787  * and all its descendants. This makes the child tab Component, and all its descendants <b>unusable</b>. To enable
42788  * re-use of a tab, configure the TabPanel with <b><code>{@link #autoDestroy autoDestroy: false}</code></b>.
42789  *
42790  * <p><b><u>TabPanel header/footer elements</u></b></p>
42791  * <p>TabPanels use their {@link Ext.Panel#header header} or {@link Ext.Panel#footer footer} element
42792  * (depending on the {@link #tabPosition} configuration) to accommodate the tab selector buttons.
42793  * This means that a TabPanel will not display any configured title, and will not display any
42794  * configured header {@link Ext.Panel#tools tools}.</p>
42795  * <p>To display a header, embed the TabPanel in a {@link Ext.Panel Panel} which uses
42796  * <b><tt>{@link Ext.Container#layout layout:'fit'}</tt></b>.</p>
42797  *
42798  * <p><b><u>Tab Events</u></b></p>
42799  * <p>There is no actual tab class &mdash; each tab is simply a {@link Ext.BoxComponent Component}
42800  * such as a {@link Ext.Panel Panel}. However, when rendered in a TabPanel, each child Component
42801  * can fire additional events that only exist for tabs and are not available from other Components.
42802  * These events are:</p>
42803  * <div><ul class="mdetail-params">
42804  * <li><tt><b>{@link Ext.Panel#activate activate}</b></tt> : Fires when this Component becomes
42805  * the active tab.</li>
42806  * <li><tt><b>{@link Ext.Panel#deactivate deactivate}</b></tt> : Fires when the Component that
42807  * was the active tab becomes deactivated.</li>
42808  * <li><tt><b>{@link Ext.Panel#beforeclose beforeclose}</b></tt> : Fires when the user clicks on the close tool of a closeable tab.
42809  * May be vetoed by returning <code>false</code> from a handler.</li>
42810  * <li><tt><b>{@link Ext.Panel#close close}</b></tt> : Fires a closeable tab has been closed by the user.</li>
42811  * </ul></div>
42812  * <p><b><u>Creating TabPanels from Code</u></b></p>
42813  * <p>TabPanels can be created and rendered completely in code, as in this example:</p>
42814  * <pre><code>
42815 var tabs = new Ext.TabPanel({
42816     renderTo: Ext.getBody(),
42817     activeTab: 0,
42818     items: [{
42819         title: 'Tab 1',
42820         html: 'A simple tab'
42821     },{
42822         title: 'Tab 2',
42823         html: 'Another one'
42824     }]
42825 });
42826 </code></pre>
42827  * <p><b><u>Creating TabPanels from Existing Markup</u></b></p>
42828  * <p>TabPanels can also be rendered from pre-existing markup in a couple of ways.</p>
42829  * <div><ul class="mdetail-params">
42830  *
42831  * <li>Pre-Structured Markup</li>
42832  * <div class="sub-desc">
42833  * <p>A container div with one or more nested tab divs with class <tt>'x-tab'</tt> can be rendered entirely
42834  * from existing markup (See the {@link #autoTabs} example).</p>
42835  * </div>
42836  *
42837  * <li>Un-Structured Markup</li>
42838  * <div class="sub-desc">
42839  * <p>A TabPanel can also be rendered from markup that is not strictly structured by simply specifying by id
42840  * which elements should be the container and the tabs. Using this method tab content can be pulled from different
42841  * elements within the page by id regardless of page structure. For example:</p>
42842  * <pre><code>
42843 var tabs = new Ext.TabPanel({
42844     renderTo: 'my-tabs',
42845     activeTab: 0,
42846     items:[
42847         {contentEl:'tab1', title:'Tab 1'},
42848         {contentEl:'tab2', title:'Tab 2'}
42849     ]
42850 });
42851
42852 // Note that the tabs do not have to be nested within the container (although they can be)
42853 &lt;div id="my-tabs">&lt;/div>
42854 &lt;div id="tab1" class="x-hide-display">A simple tab&lt;/div>
42855 &lt;div id="tab2" class="x-hide-display">Another one&lt;/div>
42856 </code></pre>
42857  * Note that the tab divs in this example contain the class <tt>'x-hide-display'</tt> so that they can be rendered
42858  * deferred without displaying outside the tabs. You could alternately set <tt>{@link #deferredRender} = false </tt>
42859  * to render all content tabs on page load.
42860  * </div>
42861  *
42862  * </ul></div>
42863  *
42864  * @extends Ext.Panel
42865  * @constructor
42866  * @param {Object} config The configuration options
42867  * @xtype tabpanel
42868  */
42869 Ext.TabPanel = Ext.extend(Ext.Panel,  {
42870     /**
42871      * @cfg {Boolean} layoutOnTabChange
42872      * Set to true to force a layout of the active tab when the tab is changed. Defaults to false.
42873      * See {@link Ext.layout.CardLayout}.<code>{@link Ext.layout.CardLayout#layoutOnCardChange layoutOnCardChange}</code>.
42874      */
42875     /**
42876      * @cfg {String} tabCls <b>This config option is used on <u>child Components</u> of ths TabPanel.</b> A CSS
42877      * class name applied to the tab strip item representing the child Component, allowing special
42878      * styling to be applied.
42879      */
42880     /**
42881      * @cfg {Boolean} deferredRender
42882      * <p><tt>true</tt> by default to defer the rendering of child <tt>{@link Ext.Container#items items}</tt>
42883      * to the browsers DOM until a tab is activated. <tt>false</tt> will render all contained
42884      * <tt>{@link Ext.Container#items items}</tt> as soon as the {@link Ext.layout.CardLayout layout}
42885      * is rendered. If there is a significant amount of content or a lot of heavy controls being
42886      * rendered into panels that are not displayed by default, setting this to <tt>true</tt> might
42887      * improve performance.</p>
42888      * <br><p>The <tt>deferredRender</tt> property is internally passed to the layout manager for
42889      * TabPanels ({@link Ext.layout.CardLayout}) as its {@link Ext.layout.CardLayout#deferredRender}
42890      * configuration value.</p>
42891      * <br><p><b>Note</b>: leaving <tt>deferredRender</tt> as <tt>true</tt> means that the content
42892      * within an unactivated tab will not be available. For example, this means that if the TabPanel
42893      * is within a {@link Ext.form.FormPanel form}, then until a tab is activated, any Fields within
42894      * unactivated tabs will not be rendered, and will therefore not be submitted and will not be
42895      * available to either {@link Ext.form.BasicForm#getValues getValues} or
42896      * {@link Ext.form.BasicForm#setValues setValues}.</p>
42897      */
42898     deferredRender : true,
42899     /**
42900      * @cfg {Number} tabWidth The initial width in pixels of each new tab (defaults to 120).
42901      */
42902     tabWidth : 120,
42903     /**
42904      * @cfg {Number} minTabWidth The minimum width in pixels for each tab when {@link #resizeTabs} = true (defaults to 30).
42905      */
42906     minTabWidth : 30,
42907     /**
42908      * @cfg {Boolean} resizeTabs True to automatically resize each tab so that the tabs will completely fill the
42909      * tab strip (defaults to false).  Setting this to true may cause specific widths that might be set per tab to
42910      * be overridden in order to fit them all into view (although {@link #minTabWidth} will always be honored).
42911      */
42912     resizeTabs : false,
42913     /**
42914      * @cfg {Boolean} enableTabScroll True to enable scrolling to tabs that may be invisible due to overflowing the
42915      * overall TabPanel width. Only available with tabPosition:'top' (defaults to false).
42916      */
42917     enableTabScroll : false,
42918     /**
42919      * @cfg {Number} scrollIncrement The number of pixels to scroll each time a tab scroll button is pressed
42920      * (defaults to <tt>100</tt>, or if <tt>{@link #resizeTabs} = true</tt>, the calculated tab width).  Only
42921      * applies when <tt>{@link #enableTabScroll} = true</tt>.
42922      */
42923     scrollIncrement : 0,
42924     /**
42925      * @cfg {Number} scrollRepeatInterval Number of milliseconds between each scroll while a tab scroll button is
42926      * continuously pressed (defaults to <tt>400</tt>).
42927      */
42928     scrollRepeatInterval : 400,
42929     /**
42930      * @cfg {Float} scrollDuration The number of milliseconds that each scroll animation should last (defaults
42931      * to <tt>.35</tt>). Only applies when <tt>{@link #animScroll} = true</tt>.
42932      */
42933     scrollDuration : 0.35,
42934     /**
42935      * @cfg {Boolean} animScroll True to animate tab scrolling so that hidden tabs slide smoothly into view (defaults
42936      * to <tt>true</tt>).  Only applies when <tt>{@link #enableTabScroll} = true</tt>.
42937      */
42938     animScroll : true,
42939     /**
42940      * @cfg {String} tabPosition The position where the tab strip should be rendered (defaults to <tt>'top'</tt>).
42941      * The only other supported value is <tt>'bottom'</tt>.  <b>Note</b>: tab scrolling is only supported for
42942      * <tt>tabPosition: 'top'</tt>.
42943      */
42944     tabPosition : 'top',
42945     /**
42946      * @cfg {String} baseCls The base CSS class applied to the panel (defaults to <tt>'x-tab-panel'</tt>).
42947      */
42948     baseCls : 'x-tab-panel',
42949     /**
42950      * @cfg {Boolean} autoTabs
42951      * <p><tt>true</tt> to query the DOM for any divs with a class of 'x-tab' to be automatically converted
42952      * to tabs and added to this panel (defaults to <tt>false</tt>).  Note that the query will be executed within
42953      * the scope of the container element only (so that multiple tab panels from markup can be supported via this
42954      * method).</p>
42955      * <p>This method is only possible when the markup is structured correctly as a container with nested divs
42956      * containing the class <tt>'x-tab'</tt>. To create TabPanels without these limitations, or to pull tab content
42957      * from other elements on the page, see the example at the top of the class for generating tabs from markup.</p>
42958      * <p>There are a couple of things to note when using this method:<ul>
42959      * <li>When using the <tt>autoTabs</tt> config (as opposed to passing individual tab configs in the TabPanel's
42960      * {@link #items} collection), you must use <tt>{@link #applyTo}</tt> to correctly use the specified <tt>id</tt>
42961      * as the tab container. The <tt>autoTabs</tt> method <em>replaces</em> existing content with the TabPanel
42962      * components.</li>
42963      * <li>Make sure that you set <tt>{@link #deferredRender}: false</tt> so that the content elements for each
42964      * tab will be rendered into the TabPanel immediately upon page load, otherwise they will not be transformed
42965      * until each tab is activated and will be visible outside the TabPanel.</li>
42966      * </ul>Example usage:</p>
42967      * <pre><code>
42968 var tabs = new Ext.TabPanel({
42969     applyTo: 'my-tabs',
42970     activeTab: 0,
42971     deferredRender: false,
42972     autoTabs: true
42973 });
42974
42975 // This markup will be converted to a TabPanel from the code above
42976 &lt;div id="my-tabs">
42977     &lt;div class="x-tab" title="Tab 1">A simple tab&lt;/div>
42978     &lt;div class="x-tab" title="Tab 2">Another one&lt;/div>
42979 &lt;/div>
42980 </code></pre>
42981      */
42982     autoTabs : false,
42983     /**
42984      * @cfg {String} autoTabSelector The CSS selector used to search for tabs in existing markup when
42985      * <tt>{@link #autoTabs} = true</tt> (defaults to <tt>'div.x-tab'</tt>).  This can be any valid selector
42986      * supported by {@link Ext.DomQuery#select}. Note that the query will be executed within the scope of this
42987      * tab panel only (so that multiple tab panels from markup can be supported on a page).
42988      */
42989     autoTabSelector : 'div.x-tab',
42990     /**
42991      * @cfg {String/Number} activeTab A string id or the numeric index of the tab that should be initially
42992      * activated on render (defaults to undefined).
42993      */
42994     activeTab : undefined,
42995     /**
42996      * @cfg {Number} tabMargin The number of pixels of space to calculate into the sizing and scrolling of
42997      * tabs. If you change the margin in CSS, you will need to update this value so calculations are correct
42998      * with either <tt>{@link #resizeTabs}</tt> or scrolling tabs. (defaults to <tt>2</tt>)
42999      */
43000     tabMargin : 2,
43001     /**
43002      * @cfg {Boolean} plain </tt>true</tt> to render the tab strip without a background container image
43003      * (defaults to <tt>false</tt>).
43004      */
43005     plain : false,
43006     /**
43007      * @cfg {Number} wheelIncrement For scrolling tabs, the number of pixels to increment on mouse wheel
43008      * scrolling (defaults to <tt>20</tt>).
43009      */
43010     wheelIncrement : 20,
43011
43012     /*
43013      * This is a protected property used when concatenating tab ids to the TabPanel id for internal uniqueness.
43014      * It does not generally need to be changed, but can be if external code also uses an id scheme that can
43015      * potentially clash with this one.
43016      */
43017     idDelimiter : '__',
43018
43019     // private
43020     itemCls : 'x-tab-item',
43021
43022     // private config overrides
43023     elements : 'body',
43024     headerAsText : false,
43025     frame : false,
43026     hideBorders :true,
43027
43028     // private
43029     initComponent : function(){
43030         this.frame = false;
43031         Ext.TabPanel.superclass.initComponent.call(this);
43032         this.addEvents(
43033             /**
43034              * @event beforetabchange
43035              * Fires before the active tab changes. Handlers can <tt>return false</tt> to cancel the tab change.
43036              * @param {TabPanel} this
43037              * @param {Panel} newTab The tab being activated
43038              * @param {Panel} currentTab The current active tab
43039              */
43040             'beforetabchange',
43041             /**
43042              * @event tabchange
43043              * Fires after the active tab has changed.
43044              * @param {TabPanel} this
43045              * @param {Panel} tab The new active tab
43046              */
43047             'tabchange',
43048             /**
43049              * @event contextmenu
43050              * Relays the contextmenu event from a tab selector element in the tab strip.
43051              * @param {TabPanel} this
43052              * @param {Panel} tab The target tab
43053              * @param {EventObject} e
43054              */
43055             'contextmenu'
43056         );
43057         /**
43058          * @cfg {Object} layoutConfig
43059          * TabPanel implicitly uses {@link Ext.layout.CardLayout} as its layout manager.
43060          * <code>layoutConfig</code> may be used to configure this layout manager.
43061          * <code>{@link #deferredRender}</code> and <code>{@link #layoutOnTabChange}</code>
43062          * configured on the TabPanel will be applied as configs to the layout manager.
43063          */
43064         this.setLayout(new Ext.layout.CardLayout(Ext.apply({
43065             layoutOnCardChange: this.layoutOnTabChange,
43066             deferredRender: this.deferredRender
43067         }, this.layoutConfig)));
43068
43069         if(this.tabPosition == 'top'){
43070             this.elements += ',header';
43071             this.stripTarget = 'header';
43072         }else {
43073             this.elements += ',footer';
43074             this.stripTarget = 'footer';
43075         }
43076         if(!this.stack){
43077             this.stack = Ext.TabPanel.AccessStack();
43078         }
43079         this.initItems();
43080     },
43081
43082     // private
43083     onRender : function(ct, position){
43084         Ext.TabPanel.superclass.onRender.call(this, ct, position);
43085
43086         if(this.plain){
43087             var pos = this.tabPosition == 'top' ? 'header' : 'footer';
43088             this[pos].addClass('x-tab-panel-'+pos+'-plain');
43089         }
43090
43091         var st = this[this.stripTarget];
43092
43093         this.stripWrap = st.createChild({cls:'x-tab-strip-wrap', cn:{
43094             tag:'ul', cls:'x-tab-strip x-tab-strip-'+this.tabPosition}});
43095
43096         var beforeEl = (this.tabPosition=='bottom' ? this.stripWrap : null);
43097         st.createChild({cls:'x-tab-strip-spacer'}, beforeEl);
43098         this.strip = new Ext.Element(this.stripWrap.dom.firstChild);
43099
43100         // create an empty span with class x-tab-strip-text to force the height of the header element when there's no tabs.
43101         this.edge = this.strip.createChild({tag:'li', cls:'x-tab-edge', cn: [{tag: 'span', cls: 'x-tab-strip-text', cn: '&#160;'}]});
43102         this.strip.createChild({cls:'x-clear'});
43103
43104         this.body.addClass('x-tab-panel-body-'+this.tabPosition);
43105
43106         /**
43107          * @cfg {Template/XTemplate} itemTpl <p>(Optional) A {@link Ext.Template Template} or
43108          * {@link Ext.XTemplate XTemplate} which may be provided to process the data object returned from
43109          * <tt>{@link #getTemplateArgs}</tt> to produce a clickable selector element in the tab strip.</p>
43110          * <p>The main element created should be a <tt>&lt;li></tt> element. In order for a click event on
43111          * a selector element to be connected to its item, it must take its <i>id</i> from the TabPanel's
43112          * native <tt>{@link #getTemplateArgs}</tt>.</p>
43113          * <p>The child element which contains the title text must be marked by the CSS class
43114          * <tt>x-tab-strip-inner</tt>.</p>
43115          * <p>To enable closability, the created element should contain an element marked by the CSS class
43116          * <tt>x-tab-strip-close</tt>.</p>
43117          * <p>If a custom <tt>itemTpl</tt> is supplied, it is the developer's responsibility to create CSS
43118          * style rules to create the desired appearance.</p>
43119          * Below is an example of how to create customized tab selector items:<pre><code>
43120 new Ext.TabPanel({
43121     renderTo: document.body,
43122     minTabWidth: 115,
43123     tabWidth: 135,
43124     enableTabScroll: true,
43125     width: 600,
43126     height: 250,
43127     defaults: {autoScroll:true},
43128     itemTpl: new Ext.XTemplate(
43129     '&lt;li class="{cls}" id="{id}" style="overflow:hidden">',
43130          '&lt;tpl if="closable">',
43131             '&lt;a class="x-tab-strip-close">&lt;/a>',
43132          '&lt;/tpl>',
43133          '&lt;a class="x-tab-right" href="#" style="padding-left:6px">',
43134             '&lt;em class="x-tab-left">',
43135                 '&lt;span class="x-tab-strip-inner">',
43136                     '&lt;img src="{src}" style="float:left;margin:3px 3px 0 0">',
43137                     '&lt;span style="margin-left:20px" class="x-tab-strip-text {iconCls}">{text} {extra}&lt;/span>',
43138                 '&lt;/span>',
43139             '&lt;/em>',
43140         '&lt;/a>',
43141     '&lt;/li>'
43142     ),
43143     getTemplateArgs: function(item) {
43144 //      Call the native method to collect the base data. Like the ID!
43145         var result = Ext.TabPanel.prototype.getTemplateArgs.call(this, item);
43146
43147 //      Add stuff used in our template
43148         return Ext.apply(result, {
43149             closable: item.closable,
43150             src: item.iconSrc,
43151             extra: item.extraText || ''
43152         });
43153     },
43154     items: [{
43155         title: 'New Tab 1',
43156         iconSrc: '../shared/icons/fam/grid.png',
43157         html: 'Tab Body 1',
43158         closable: true
43159     }, {
43160         title: 'New Tab 2',
43161         iconSrc: '../shared/icons/fam/grid.png',
43162         html: 'Tab Body 2',
43163         extraText: 'Extra stuff in the tab button'
43164     }]
43165 });
43166 </code></pre>
43167          */
43168         if(!this.itemTpl){
43169             var tt = new Ext.Template(
43170                  '<li class="{cls}" id="{id}"><a class="x-tab-strip-close"></a>',
43171                  '<a class="x-tab-right" href="#"><em class="x-tab-left">',
43172                  '<span class="x-tab-strip-inner"><span class="x-tab-strip-text {iconCls}">{text}</span></span>',
43173                  '</em></a></li>'
43174             );
43175             tt.disableFormats = true;
43176             tt.compile();
43177             Ext.TabPanel.prototype.itemTpl = tt;
43178         }
43179
43180         this.items.each(this.initTab, this);
43181     },
43182
43183     // private
43184     afterRender : function(){
43185         Ext.TabPanel.superclass.afterRender.call(this);
43186         if(this.autoTabs){
43187             this.readTabs(false);
43188         }
43189         if(this.activeTab !== undefined){
43190             var item = Ext.isObject(this.activeTab) ? this.activeTab : this.items.get(this.activeTab);
43191             delete this.activeTab;
43192             this.setActiveTab(item);
43193         }
43194     },
43195
43196     // private
43197     initEvents : function(){
43198         Ext.TabPanel.superclass.initEvents.call(this);
43199         this.mon(this.strip, {
43200             scope: this,
43201             mousedown: this.onStripMouseDown,
43202             contextmenu: this.onStripContextMenu
43203         });
43204         if(this.enableTabScroll){
43205             this.mon(this.strip, 'mousewheel', this.onWheel, this);
43206         }
43207     },
43208
43209     // private
43210     findTargets : function(e){
43211         var item = null,
43212             itemEl = e.getTarget('li:not(.x-tab-edge)', this.strip);
43213
43214         if(itemEl){
43215             item = this.getComponent(itemEl.id.split(this.idDelimiter)[1]);
43216             if(item.disabled){
43217                 return {
43218                     close : null,
43219                     item : null,
43220                     el : null
43221                 };
43222             }
43223         }
43224         return {
43225             close : e.getTarget('.x-tab-strip-close', this.strip),
43226             item : item,
43227             el : itemEl
43228         };
43229     },
43230
43231     // private
43232     onStripMouseDown : function(e){
43233         if(e.button !== 0){
43234             return;
43235         }
43236         e.preventDefault();
43237         var t = this.findTargets(e);
43238         if(t.close){
43239             if (t.item.fireEvent('beforeclose', t.item) !== false) {
43240                 t.item.fireEvent('close', t.item);
43241                 this.remove(t.item);
43242             }
43243             return;
43244         }
43245         if(t.item && t.item != this.activeTab){
43246             this.setActiveTab(t.item);
43247         }
43248     },
43249
43250     // private
43251     onStripContextMenu : function(e){
43252         e.preventDefault();
43253         var t = this.findTargets(e);
43254         if(t.item){
43255             this.fireEvent('contextmenu', this, t.item, e);
43256         }
43257     },
43258
43259     /**
43260      * True to scan the markup in this tab panel for <tt>{@link #autoTabs}</tt> using the
43261      * <tt>{@link #autoTabSelector}</tt>
43262      * @param {Boolean} removeExisting True to remove existing tabs
43263      */
43264     readTabs : function(removeExisting){
43265         if(removeExisting === true){
43266             this.items.each(function(item){
43267                 this.remove(item);
43268             }, this);
43269         }
43270         var tabs = this.el.query(this.autoTabSelector);
43271         for(var i = 0, len = tabs.length; i < len; i++){
43272             var tab = tabs[i],
43273                 title = tab.getAttribute('title');
43274             tab.removeAttribute('title');
43275             this.add({
43276                 title: title,
43277                 contentEl: tab
43278             });
43279         }
43280     },
43281
43282     // private
43283     initTab : function(item, index){
43284         var before = this.strip.dom.childNodes[index],
43285             p = this.getTemplateArgs(item),
43286             el = before ?
43287                  this.itemTpl.insertBefore(before, p) :
43288                  this.itemTpl.append(this.strip, p),
43289             cls = 'x-tab-strip-over',
43290             tabEl = Ext.get(el);
43291
43292         tabEl.hover(function(){
43293             if(!item.disabled){
43294                 tabEl.addClass(cls);
43295             }
43296         }, function(){
43297             tabEl.removeClass(cls);
43298         });
43299
43300         if(item.tabTip){
43301             tabEl.child('span.x-tab-strip-text', true).qtip = item.tabTip;
43302         }
43303         item.tabEl = el;
43304
43305         // Route *keyboard triggered* click events to the tab strip mouse handler.
43306         tabEl.select('a').on('click', function(e){
43307             if(!e.getPageX()){
43308                 this.onStripMouseDown(e);
43309             }
43310         }, this, {preventDefault: true});
43311
43312         item.on({
43313             scope: this,
43314             disable: this.onItemDisabled,
43315             enable: this.onItemEnabled,
43316             titlechange: this.onItemTitleChanged,
43317             iconchange: this.onItemIconChanged,
43318             beforeshow: this.onBeforeShowItem
43319         });
43320     },
43321
43322
43323
43324     /**
43325      * <p>Provides template arguments for rendering a tab selector item in the tab strip.</p>
43326      * <p>This method returns an object hash containing properties used by the TabPanel's <tt>{@link #itemTpl}</tt>
43327      * to create a formatted, clickable tab selector element. The properties which must be returned
43328      * are:</p><div class="mdetail-params"><ul>
43329      * <li><b>id</b> : String<div class="sub-desc">A unique identifier which links to the item</div></li>
43330      * <li><b>text</b> : String<div class="sub-desc">The text to display</div></li>
43331      * <li><b>cls</b> : String<div class="sub-desc">The CSS class name</div></li>
43332      * <li><b>iconCls</b> : String<div class="sub-desc">A CSS class to provide appearance for an icon.</div></li>
43333      * </ul></div>
43334      * @param {BoxComponent} item The {@link Ext.BoxComponent BoxComponent} for which to create a selector element in the tab strip.
43335      * @return {Object} An object hash containing the properties required to render the selector element.
43336      */
43337     getTemplateArgs : function(item) {
43338         var cls = item.closable ? 'x-tab-strip-closable' : '';
43339         if(item.disabled){
43340             cls += ' x-item-disabled';
43341         }
43342         if(item.iconCls){
43343             cls += ' x-tab-with-icon';
43344         }
43345         if(item.tabCls){
43346             cls += ' ' + item.tabCls;
43347         }
43348
43349         return {
43350             id: this.id + this.idDelimiter + item.getItemId(),
43351             text: item.title,
43352             cls: cls,
43353             iconCls: item.iconCls || ''
43354         };
43355     },
43356
43357     // private
43358     onAdd : function(c){
43359         Ext.TabPanel.superclass.onAdd.call(this, c);
43360         if(this.rendered){
43361             var items = this.items;
43362             this.initTab(c, items.indexOf(c));
43363             if(items.getCount() == 1){
43364                 this.syncSize();
43365             }
43366             this.delegateUpdates();
43367         }
43368     },
43369
43370     // private
43371     onBeforeAdd : function(item){
43372         var existing = item.events ? (this.items.containsKey(item.getItemId()) ? item : null) : this.items.get(item);
43373         if(existing){
43374             this.setActiveTab(item);
43375             return false;
43376         }
43377         Ext.TabPanel.superclass.onBeforeAdd.apply(this, arguments);
43378         var es = item.elements;
43379         item.elements = es ? es.replace(',header', '') : es;
43380         item.border = (item.border === true);
43381     },
43382
43383     // private
43384     onRemove : function(c){
43385         var te = Ext.get(c.tabEl);
43386         // check if the tabEl exists, it won't if the tab isn't rendered
43387         if(te){
43388             te.select('a').removeAllListeners();
43389             Ext.destroy(te);
43390         }
43391         Ext.TabPanel.superclass.onRemove.call(this, c);
43392         this.stack.remove(c);
43393         delete c.tabEl;
43394         c.un('disable', this.onItemDisabled, this);
43395         c.un('enable', this.onItemEnabled, this);
43396         c.un('titlechange', this.onItemTitleChanged, this);
43397         c.un('iconchange', this.onItemIconChanged, this);
43398         c.un('beforeshow', this.onBeforeShowItem, this);
43399         if(c == this.activeTab){
43400             var next = this.stack.next();
43401             if(next){
43402                 this.setActiveTab(next);
43403             }else if(this.items.getCount() > 0){
43404                 this.setActiveTab(0);
43405             }else{
43406                 this.setActiveTab(null);
43407             }
43408         }
43409         if(!this.destroying){
43410             this.delegateUpdates();
43411         }
43412     },
43413
43414     // private
43415     onBeforeShowItem : function(item){
43416         if(item != this.activeTab){
43417             this.setActiveTab(item);
43418             return false;
43419         }
43420     },
43421
43422     // private
43423     onItemDisabled : function(item){
43424         var el = this.getTabEl(item);
43425         if(el){
43426             Ext.fly(el).addClass('x-item-disabled');
43427         }
43428         this.stack.remove(item);
43429     },
43430
43431     // private
43432     onItemEnabled : function(item){
43433         var el = this.getTabEl(item);
43434         if(el){
43435             Ext.fly(el).removeClass('x-item-disabled');
43436         }
43437     },
43438
43439     // private
43440     onItemTitleChanged : function(item){
43441         var el = this.getTabEl(item);
43442         if(el){
43443             Ext.fly(el).child('span.x-tab-strip-text', true).innerHTML = item.title;
43444         }
43445     },
43446
43447     //private
43448     onItemIconChanged : function(item, iconCls, oldCls){
43449         var el = this.getTabEl(item);
43450         if(el){
43451             el = Ext.get(el);
43452             el.child('span.x-tab-strip-text').replaceClass(oldCls, iconCls);
43453             el[Ext.isEmpty(iconCls) ? 'removeClass' : 'addClass']('x-tab-with-icon');
43454         }
43455     },
43456
43457     /**
43458      * Gets the DOM element for the tab strip item which activates the child panel with the specified
43459      * ID. Access this to change the visual treatment of the item, for example by changing the CSS class name.
43460      * @param {Panel/Number/String} tab The tab component, or the tab's index, or the tabs id or itemId.
43461      * @return {HTMLElement} The DOM node
43462      */
43463     getTabEl : function(item){
43464         var c = this.getComponent(item);
43465         return c ? c.tabEl : null;
43466     },
43467
43468     // private
43469     onResize : function(){
43470         Ext.TabPanel.superclass.onResize.apply(this, arguments);
43471         this.delegateUpdates();
43472     },
43473
43474     /**
43475      * Suspends any internal calculations or scrolling while doing a bulk operation. See {@link #endUpdate}
43476      */
43477     beginUpdate : function(){
43478         this.suspendUpdates = true;
43479     },
43480
43481     /**
43482      * Resumes calculations and scrolling at the end of a bulk operation. See {@link #beginUpdate}
43483      */
43484     endUpdate : function(){
43485         this.suspendUpdates = false;
43486         this.delegateUpdates();
43487     },
43488
43489     /**
43490      * Hides the tab strip item for the passed tab
43491      * @param {Number/String/Panel} item The tab index, id or item
43492      */
43493     hideTabStripItem : function(item){
43494         item = this.getComponent(item);
43495         var el = this.getTabEl(item);
43496         if(el){
43497             el.style.display = 'none';
43498             this.delegateUpdates();
43499         }
43500         this.stack.remove(item);
43501     },
43502
43503     /**
43504      * Unhides the tab strip item for the passed tab
43505      * @param {Number/String/Panel} item The tab index, id or item
43506      */
43507     unhideTabStripItem : function(item){
43508         item = this.getComponent(item);
43509         var el = this.getTabEl(item);
43510         if(el){
43511             el.style.display = '';
43512             this.delegateUpdates();
43513         }
43514     },
43515
43516     // private
43517     delegateUpdates : function(){
43518         if(this.suspendUpdates){
43519             return;
43520         }
43521         if(this.resizeTabs && this.rendered){
43522             this.autoSizeTabs();
43523         }
43524         if(this.enableTabScroll && this.rendered){
43525             this.autoScrollTabs();
43526         }
43527     },
43528
43529     // private
43530     autoSizeTabs : function(){
43531         var count = this.items.length,
43532             ce = this.tabPosition != 'bottom' ? 'header' : 'footer',
43533             ow = this[ce].dom.offsetWidth,
43534             aw = this[ce].dom.clientWidth;
43535
43536         if(!this.resizeTabs || count < 1 || !aw){ // !aw for display:none
43537             return;
43538         }
43539
43540         var each = Math.max(Math.min(Math.floor((aw-4) / count) - this.tabMargin, this.tabWidth), this.minTabWidth); // -4 for float errors in IE
43541         this.lastTabWidth = each;
43542         var lis = this.strip.query('li:not(.x-tab-edge)');
43543         for(var i = 0, len = lis.length; i < len; i++) {
43544             var li = lis[i],
43545                 inner = Ext.fly(li).child('.x-tab-strip-inner', true),
43546                 tw = li.offsetWidth,
43547                 iw = inner.offsetWidth;
43548             inner.style.width = (each - (tw-iw)) + 'px';
43549         }
43550     },
43551
43552     // private
43553     adjustBodyWidth : function(w){
43554         if(this.header){
43555             this.header.setWidth(w);
43556         }
43557         if(this.footer){
43558             this.footer.setWidth(w);
43559         }
43560         return w;
43561     },
43562
43563     /**
43564      * Sets the specified tab as the active tab. This method fires the {@link #beforetabchange} event which
43565      * can <tt>return false</tt> to cancel the tab change.
43566      * @param {String/Number} item
43567      * The id or tab Panel to activate. This parameter may be any of the following:
43568      * <div><ul class="mdetail-params">
43569      * <li>a <b><tt>String</tt></b> : representing the <code>{@link Ext.Component#itemId itemId}</code>
43570      * or <code>{@link Ext.Component#id id}</code> of the child component </li>
43571      * <li>a <b><tt>Number</tt></b> : representing the position of the child component
43572      * within the <code>{@link Ext.Container#items items}</code> <b>property</b></li>
43573      * </ul></div>
43574      * <p>For additional information see {@link Ext.util.MixedCollection#get}.
43575      */
43576     setActiveTab : function(item){
43577         item = this.getComponent(item);
43578         if(this.fireEvent('beforetabchange', this, item, this.activeTab) === false){
43579             return;
43580         }
43581         if(!this.rendered){
43582             this.activeTab = item;
43583             return;
43584         }
43585         if(this.activeTab != item){
43586             if(this.activeTab){
43587                 var oldEl = this.getTabEl(this.activeTab);
43588                 if(oldEl){
43589                     Ext.fly(oldEl).removeClass('x-tab-strip-active');
43590                 }
43591             }
43592             if(item){
43593                 var el = this.getTabEl(item);
43594                 Ext.fly(el).addClass('x-tab-strip-active');
43595                 this.activeTab = item;
43596                 this.stack.add(item);
43597
43598                 this.layout.setActiveItem(item);
43599                 if(this.scrolling){
43600                     this.scrollToTab(item, this.animScroll);
43601                 }
43602             }
43603             this.fireEvent('tabchange', this, item);
43604         }
43605     },
43606
43607     /**
43608      * Returns the Component which is the currently active tab. <b>Note that before the TabPanel
43609      * first activates a child Component, this method will return whatever was configured in the
43610      * {@link #activeTab} config option.</b>
43611      * @return {BoxComponent} The currently active child Component if one <i>is</i> active, or the {@link #activeTab} config value.
43612      */
43613     getActiveTab : function(){
43614         return this.activeTab || null;
43615     },
43616
43617     /**
43618      * Gets the specified tab by id.
43619      * @param {String} id The tab id
43620      * @return {Panel} The tab
43621      */
43622     getItem : function(item){
43623         return this.getComponent(item);
43624     },
43625
43626     // private
43627     autoScrollTabs : function(){
43628         this.pos = this.tabPosition=='bottom' ? this.footer : this.header;
43629         var count = this.items.length,
43630             ow = this.pos.dom.offsetWidth,
43631             tw = this.pos.dom.clientWidth,
43632             wrap = this.stripWrap,
43633             wd = wrap.dom,
43634             cw = wd.offsetWidth,
43635             pos = this.getScrollPos(),
43636             l = this.edge.getOffsetsTo(this.stripWrap)[0] + pos;
43637
43638         if(!this.enableTabScroll || count < 1 || cw < 20){ // 20 to prevent display:none issues
43639             return;
43640         }
43641         if(l <= tw){
43642             wd.scrollLeft = 0;
43643             wrap.setWidth(tw);
43644             if(this.scrolling){
43645                 this.scrolling = false;
43646                 this.pos.removeClass('x-tab-scrolling');
43647                 this.scrollLeft.hide();
43648                 this.scrollRight.hide();
43649                 // See here: http://extjs.com/forum/showthread.php?t=49308&highlight=isSafari
43650                 if(Ext.isAir || Ext.isWebKit){
43651                     wd.style.marginLeft = '';
43652                     wd.style.marginRight = '';
43653                 }
43654             }
43655         }else{
43656             if(!this.scrolling){
43657                 this.pos.addClass('x-tab-scrolling');
43658                 // See here: http://extjs.com/forum/showthread.php?t=49308&highlight=isSafari
43659                 if(Ext.isAir || Ext.isWebKit){
43660                     wd.style.marginLeft = '18px';
43661                     wd.style.marginRight = '18px';
43662                 }
43663             }
43664             tw -= wrap.getMargins('lr');
43665             wrap.setWidth(tw > 20 ? tw : 20);
43666             if(!this.scrolling){
43667                 if(!this.scrollLeft){
43668                     this.createScrollers();
43669                 }else{
43670                     this.scrollLeft.show();
43671                     this.scrollRight.show();
43672                 }
43673             }
43674             this.scrolling = true;
43675             if(pos > (l-tw)){ // ensure it stays within bounds
43676                 wd.scrollLeft = l-tw;
43677             }else{ // otherwise, make sure the active tab is still visible
43678                 this.scrollToTab(this.activeTab, false);
43679             }
43680             this.updateScrollButtons();
43681         }
43682     },
43683
43684     // private
43685     createScrollers : function(){
43686         this.pos.addClass('x-tab-scrolling-' + this.tabPosition);
43687         var h = this.stripWrap.dom.offsetHeight;
43688
43689         // left
43690         var sl = this.pos.insertFirst({
43691             cls:'x-tab-scroller-left'
43692         });
43693         sl.setHeight(h);
43694         sl.addClassOnOver('x-tab-scroller-left-over');
43695         this.leftRepeater = new Ext.util.ClickRepeater(sl, {
43696             interval : this.scrollRepeatInterval,
43697             handler: this.onScrollLeft,
43698             scope: this
43699         });
43700         this.scrollLeft = sl;
43701
43702         // right
43703         var sr = this.pos.insertFirst({
43704             cls:'x-tab-scroller-right'
43705         });
43706         sr.setHeight(h);
43707         sr.addClassOnOver('x-tab-scroller-right-over');
43708         this.rightRepeater = new Ext.util.ClickRepeater(sr, {
43709             interval : this.scrollRepeatInterval,
43710             handler: this.onScrollRight,
43711             scope: this
43712         });
43713         this.scrollRight = sr;
43714     },
43715
43716     // private
43717     getScrollWidth : function(){
43718         return this.edge.getOffsetsTo(this.stripWrap)[0] + this.getScrollPos();
43719     },
43720
43721     // private
43722     getScrollPos : function(){
43723         return parseInt(this.stripWrap.dom.scrollLeft, 10) || 0;
43724     },
43725
43726     // private
43727     getScrollArea : function(){
43728         return parseInt(this.stripWrap.dom.clientWidth, 10) || 0;
43729     },
43730
43731     // private
43732     getScrollAnim : function(){
43733         return {duration:this.scrollDuration, callback: this.updateScrollButtons, scope: this};
43734     },
43735
43736     // private
43737     getScrollIncrement : function(){
43738         return this.scrollIncrement || (this.resizeTabs ? this.lastTabWidth+2 : 100);
43739     },
43740
43741     /**
43742      * Scrolls to a particular tab if tab scrolling is enabled
43743      * @param {Panel} item The item to scroll to
43744      * @param {Boolean} animate True to enable animations
43745      */
43746
43747     scrollToTab : function(item, animate){
43748         if(!item){
43749             return;
43750         }
43751         var el = this.getTabEl(item),
43752             pos = this.getScrollPos(),
43753             area = this.getScrollArea(),
43754             left = Ext.fly(el).getOffsetsTo(this.stripWrap)[0] + pos,
43755             right = left + el.offsetWidth;
43756         if(left < pos){
43757             this.scrollTo(left, animate);
43758         }else if(right > (pos + area)){
43759             this.scrollTo(right - area, animate);
43760         }
43761     },
43762
43763     // private
43764     scrollTo : function(pos, animate){
43765         this.stripWrap.scrollTo('left', pos, animate ? this.getScrollAnim() : false);
43766         if(!animate){
43767             this.updateScrollButtons();
43768         }
43769     },
43770
43771     onWheel : function(e){
43772         var d = e.getWheelDelta()*this.wheelIncrement*-1;
43773         e.stopEvent();
43774
43775         var pos = this.getScrollPos(),
43776             newpos = pos + d,
43777             sw = this.getScrollWidth()-this.getScrollArea();
43778
43779         var s = Math.max(0, Math.min(sw, newpos));
43780         if(s != pos){
43781             this.scrollTo(s, false);
43782         }
43783     },
43784
43785     // private
43786     onScrollRight : function(){
43787         var sw = this.getScrollWidth()-this.getScrollArea(),
43788             pos = this.getScrollPos(),
43789             s = Math.min(sw, pos + this.getScrollIncrement());
43790         if(s != pos){
43791             this.scrollTo(s, this.animScroll);
43792         }
43793     },
43794
43795     // private
43796     onScrollLeft : function(){
43797         var pos = this.getScrollPos(),
43798             s = Math.max(0, pos - this.getScrollIncrement());
43799         if(s != pos){
43800             this.scrollTo(s, this.animScroll);
43801         }
43802     },
43803
43804     // private
43805     updateScrollButtons : function(){
43806         var pos = this.getScrollPos();
43807         this.scrollLeft[pos === 0 ? 'addClass' : 'removeClass']('x-tab-scroller-left-disabled');
43808         this.scrollRight[pos >= (this.getScrollWidth()-this.getScrollArea()) ? 'addClass' : 'removeClass']('x-tab-scroller-right-disabled');
43809     },
43810
43811     // private
43812     beforeDestroy : function() {
43813         Ext.destroy(this.leftRepeater, this.rightRepeater);
43814         this.deleteMembers('strip', 'edge', 'scrollLeft', 'scrollRight', 'stripWrap');
43815         this.activeTab = null;
43816         Ext.TabPanel.superclass.beforeDestroy.apply(this);
43817     }
43818
43819     /**
43820      * @cfg {Boolean} collapsible
43821      * @hide
43822      */
43823     /**
43824      * @cfg {String} header
43825      * @hide
43826      */
43827     /**
43828      * @cfg {Boolean} headerAsText
43829      * @hide
43830      */
43831     /**
43832      * @property header
43833      * @hide
43834      */
43835     /**
43836      * @cfg title
43837      * @hide
43838      */
43839     /**
43840      * @cfg {Array} tools
43841      * @hide
43842      */
43843     /**
43844      * @cfg {Array} toolTemplate
43845      * @hide
43846      */
43847     /**
43848      * @cfg {Boolean} hideCollapseTool
43849      * @hide
43850      */
43851     /**
43852      * @cfg {Boolean} titleCollapse
43853      * @hide
43854      */
43855     /**
43856      * @cfg {Boolean} collapsed
43857      * @hide
43858      */
43859     /**
43860      * @cfg {String} layout
43861      * @hide
43862      */
43863     /**
43864      * @cfg {Boolean} preventBodyReset
43865      * @hide
43866      */
43867 });
43868 Ext.reg('tabpanel', Ext.TabPanel);
43869
43870 /**
43871  * See {@link #setActiveTab}. Sets the specified tab as the active tab. This method fires
43872  * the {@link #beforetabchange} event which can <tt>return false</tt> to cancel the tab change.
43873  * @param {String/Panel} tab The id or tab Panel to activate
43874  * @method activate
43875  */
43876 Ext.TabPanel.prototype.activate = Ext.TabPanel.prototype.setActiveTab;
43877
43878 // private utility class used by TabPanel
43879 Ext.TabPanel.AccessStack = function(){
43880     var items = [];
43881     return {
43882         add : function(item){
43883             items.push(item);
43884             if(items.length > 10){
43885                 items.shift();
43886             }
43887         },
43888
43889         remove : function(item){
43890             var s = [];
43891             for(var i = 0, len = items.length; i < len; i++) {
43892                 if(items[i] != item){
43893                     s.push(items[i]);
43894                 }
43895             }
43896             items = s;
43897         },
43898
43899         next : function(){
43900             return items.pop();
43901         }
43902     };
43903 };
43904 /**\r
43905  * @class Ext.Button\r
43906  * @extends Ext.BoxComponent\r
43907  * Simple Button class\r
43908  * @cfg {String} text The button text to be used as innerHTML (html tags are accepted)\r
43909  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image\r
43910  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:'x-btn-text-icon')\r
43911  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event).\r
43912  * The handler is passed the following parameters:<div class="mdetail-params"><ul>\r
43913  * <li><code>b</code> : Button<div class="sub-desc">This Button.</div></li>\r
43914  * <li><code>e</code> : EventObject<div class="sub-desc">The click event.</div></li>\r
43915  * </ul></div>\r
43916  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width).\r
43917  * See also {@link Ext.Panel}.<tt>{@link Ext.Panel#minButtonWidth minButtonWidth}</tt>.\r
43918  * @cfg {String/Object} tooltip The tooltip for the button - can be a string to be used as innerHTML (html tags are accepted) or QuickTips config object\r
43919  * @cfg {Boolean} hidden True to start hidden (defaults to false)\r
43920  * @cfg {Boolean} disabled True to start disabled (defaults to false)\r
43921  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)\r
43922  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed)\r
43923  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be\r
43924  * a {@link Ext.util.ClickRepeater ClickRepeater} config object (defaults to false).\r
43925  * @constructor\r
43926  * Create a new button\r
43927  * @param {Object} config The config object\r
43928  * @xtype button\r
43929  */\r
43930 Ext.Button = Ext.extend(Ext.BoxComponent, {\r
43931     /**\r
43932      * Read-only. True if this button is hidden\r
43933      * @type Boolean\r
43934      */\r
43935     hidden : false,\r
43936     /**\r
43937      * Read-only. True if this button is disabled\r
43938      * @type Boolean\r
43939      */\r
43940     disabled : false,\r
43941     /**\r
43942      * Read-only. True if this button is pressed (only if enableToggle = true)\r
43943      * @type Boolean\r
43944      */\r
43945     pressed : false,\r
43946 \r
43947     /**\r
43948      * @cfg {Number} tabIndex Set a DOM tabIndex for this button (defaults to undefined)\r
43949      */\r
43950 \r
43951     /**\r
43952      * @cfg {Boolean} allowDepress\r
43953      * False to not allow a pressed Button to be depressed (defaults to undefined). Only valid when {@link #enableToggle} is true.\r
43954      */\r
43955 \r
43956     /**\r
43957      * @cfg {Boolean} enableToggle\r
43958      * True to enable pressed/not pressed toggling (defaults to false)\r
43959      */\r
43960     enableToggle : false,\r
43961     /**\r
43962      * @cfg {Function} toggleHandler\r
43963      * Function called when a Button with {@link #enableToggle} set to true is clicked. Two arguments are passed:<ul class="mdetail-params">\r
43964      * <li><b>button</b> : Ext.Button<div class="sub-desc">this Button object</div></li>\r
43965      * <li><b>state</b> : Boolean<div class="sub-desc">The next state of the Button, true means pressed.</div></li>\r
43966      * </ul>\r
43967      */\r
43968     /**\r
43969      * @cfg {Mixed} menu\r
43970      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).\r
43971      */\r
43972     /**\r
43973      * @cfg {String} menuAlign\r
43974      * The position to align the menu to (see {@link Ext.Element#alignTo} for more details, defaults to 'tl-bl?').\r
43975      */\r
43976     menuAlign : 'tl-bl?',\r
43977 \r
43978     /**\r
43979      * @cfg {String} overflowText If used in a {@link Ext.Toolbar Toolbar}, the\r
43980      * text to be used if this item is shown in the overflow menu. See also\r
43981      * {@link Ext.Toolbar.Item}.<code>{@link Ext.Toolbar.Item#overflowText overflowText}</code>.\r
43982      */\r
43983     /**\r
43984      * @cfg {String} iconCls\r
43985      * A css class which sets a background image to be used as the icon for this button\r
43986      */\r
43987     /**\r
43988      * @cfg {String} type\r
43989      * submit, reset or button - defaults to 'button'\r
43990      */\r
43991     type : 'button',\r
43992 \r
43993     // private\r
43994     menuClassTarget : 'tr:nth(2)',\r
43995 \r
43996     /**\r
43997      * @cfg {String} clickEvent\r
43998      * The DOM event that will fire the handler of the button. This can be any valid event name (dblclick, contextmenu).\r
43999      * Defaults to <tt>'click'</tt>.\r
44000      */\r
44001     clickEvent : 'click',\r
44002 \r
44003     /**\r
44004      * @cfg {Boolean} handleMouseEvents\r
44005      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)\r
44006      */\r
44007     handleMouseEvents : true,\r
44008 \r
44009     /**\r
44010      * @cfg {String} tooltipType\r
44011      * The type of tooltip to use. Either 'qtip' (default) for QuickTips or 'title' for title attribute.\r
44012      */\r
44013     tooltipType : 'qtip',\r
44014 \r
44015     /**\r
44016      * @cfg {String} buttonSelector\r
44017      * <p>(Optional) A {@link Ext.DomQuery DomQuery} selector which is used to extract the active, clickable element from the\r
44018      * DOM structure created.</p>\r
44019      * <p>When a custom {@link #template} is used, you  must ensure that this selector results in the selection of\r
44020      * a focussable element.</p>\r
44021      * <p>Defaults to <b><tt>'button:first-child'</tt></b>.</p>\r
44022      */\r
44023     buttonSelector : 'button:first-child',\r
44024 \r
44025     /**\r
44026      * @cfg {String} scale\r
44027      * <p>(Optional) The size of the Button. Three values are allowed:</p>\r
44028      * <ul class="mdetail-params">\r
44029      * <li>'small'<div class="sub-desc">Results in the button element being 16px high.</div></li>\r
44030      * <li>'medium'<div class="sub-desc">Results in the button element being 24px high.</div></li>\r
44031      * <li>'large'<div class="sub-desc">Results in the button element being 32px high.</div></li>\r
44032      * </ul>\r
44033      * <p>Defaults to <b><tt>'small'</tt></b>.</p>\r
44034      */\r
44035     scale : 'small',\r
44036 \r
44037     /**\r
44038      * @cfg {Object} scope The scope (<tt><b>this</b></tt> reference) in which the\r
44039      * <code>{@link #handler}</code> and <code>{@link #toggleHandler}</code> is\r
44040      * executed. Defaults to this Button.\r
44041      */\r
44042 \r
44043     /**\r
44044      * @cfg {String} iconAlign\r
44045      * <p>(Optional) The side of the Button box to render the icon. Four values are allowed:</p>\r
44046      * <ul class="mdetail-params">\r
44047      * <li>'top'<div class="sub-desc"></div></li>\r
44048      * <li>'right'<div class="sub-desc"></div></li>\r
44049      * <li>'bottom'<div class="sub-desc"></div></li>\r
44050      * <li>'left'<div class="sub-desc"></div></li>\r
44051      * </ul>\r
44052      * <p>Defaults to <b><tt>'left'</tt></b>.</p>\r
44053      */\r
44054     iconAlign : 'left',\r
44055 \r
44056     /**\r
44057      * @cfg {String} arrowAlign\r
44058      * <p>(Optional) The side of the Button box to render the arrow if the button has an associated {@link #menu}.\r
44059      * Two values are allowed:</p>\r
44060      * <ul class="mdetail-params">\r
44061      * <li>'right'<div class="sub-desc"></div></li>\r
44062      * <li>'bottom'<div class="sub-desc"></div></li>\r
44063      * </ul>\r
44064      * <p>Defaults to <b><tt>'right'</tt></b>.</p>\r
44065      */\r
44066     arrowAlign : 'right',\r
44067 \r
44068     /**\r
44069      * @cfg {Ext.Template} template (Optional)\r
44070      * <p>A {@link Ext.Template Template} used to create the Button's DOM structure.</p>\r
44071      * Instances, or subclasses which need a different DOM structure may provide a different\r
44072      * template layout in conjunction with an implementation of {@link #getTemplateArgs}.\r
44073      * @type Ext.Template\r
44074      * @property template\r
44075      */\r
44076     /**\r
44077      * @cfg {String} cls\r
44078      * A CSS class string to apply to the button's main element.\r
44079      */\r
44080     /**\r
44081      * @property menu\r
44082      * @type Menu\r
44083      * The {@link Ext.menu.Menu Menu} object associated with this Button when configured with the {@link #menu} config option.\r
44084      */\r
44085 \r
44086     initComponent : function(){\r
44087         Ext.Button.superclass.initComponent.call(this);\r
44088 \r
44089         this.addEvents(\r
44090             /**\r
44091              * @event click\r
44092              * Fires when this button is clicked\r
44093              * @param {Button} this\r
44094              * @param {EventObject} e The click event\r
44095              */\r
44096             'click',\r
44097             /**\r
44098              * @event toggle\r
44099              * Fires when the 'pressed' state of this button changes (only if enableToggle = true)\r
44100              * @param {Button} this\r
44101              * @param {Boolean} pressed\r
44102              */\r
44103             'toggle',\r
44104             /**\r
44105              * @event mouseover\r
44106              * Fires when the mouse hovers over the button\r
44107              * @param {Button} this\r
44108              * @param {Event} e The event object\r
44109              */\r
44110             'mouseover',\r
44111             /**\r
44112              * @event mouseout\r
44113              * Fires when the mouse exits the button\r
44114              * @param {Button} this\r
44115              * @param {Event} e The event object\r
44116              */\r
44117             'mouseout',\r
44118             /**\r
44119              * @event menushow\r
44120              * If this button has a menu, this event fires when it is shown\r
44121              * @param {Button} this\r
44122              * @param {Menu} menu\r
44123              */\r
44124             'menushow',\r
44125             /**\r
44126              * @event menuhide\r
44127              * If this button has a menu, this event fires when it is hidden\r
44128              * @param {Button} this\r
44129              * @param {Menu} menu\r
44130              */\r
44131             'menuhide',\r
44132             /**\r
44133              * @event menutriggerover\r
44134              * If this button has a menu, this event fires when the mouse enters the menu triggering element\r
44135              * @param {Button} this\r
44136              * @param {Menu} menu\r
44137              * @param {EventObject} e\r
44138              */\r
44139             'menutriggerover',\r
44140             /**\r
44141              * @event menutriggerout\r
44142              * If this button has a menu, this event fires when the mouse leaves the menu triggering element\r
44143              * @param {Button} this\r
44144              * @param {Menu} menu\r
44145              * @param {EventObject} e\r
44146              */\r
44147             'menutriggerout'\r
44148         );\r
44149         if(this.menu){\r
44150             this.menu = Ext.menu.MenuMgr.get(this.menu);\r
44151         }\r
44152         if(Ext.isString(this.toggleGroup)){\r
44153             this.enableToggle = true;\r
44154         }\r
44155     },\r
44156 \r
44157 /**\r
44158   * <p>This method returns an Array which provides substitution parameters for the {@link #template Template} used\r
44159   * to create this Button's DOM structure.</p>\r
44160   * <p>Instances or subclasses which use a different Template to create a different DOM structure may need to provide their\r
44161   * own implementation of this method.</p>\r
44162   * <p>The default implementation which provides data for the default {@link #template} returns an Array containing the\r
44163   * following items:</p><div class="mdetail-params"><ul>\r
44164   * <li>The &lt;button&gt;'s {@link #type}</li>\r
44165   * <li>A CSS class name applied to the Button's main &lt;tbody&gt; element which determines the button's scale and icon alignment.</li>\r
44166   * <li>A CSS class to determine the presence and position of an arrow icon. (<code>'x-btn-arrow'</code> or <code>'x-btn-arrow-bottom'</code> or <code>''</code>)</li>\r
44167   * <li>The {@link #cls} CSS class name applied to the button's wrapping &lt;table&gt; element.</li>\r
44168   * <li>The Component id which is applied to the button's wrapping &lt;table&gt; element.</li>\r
44169   * </ul></div>\r
44170   * @return {Array} Substitution data for a Template.\r
44171  */\r
44172     getTemplateArgs : function(){\r
44173         return [this.type, 'x-btn-' + this.scale + ' x-btn-icon-' + this.scale + '-' + this.iconAlign, this.getMenuClass(), this.cls, this.id];\r
44174     },\r
44175 \r
44176     // private\r
44177     setButtonClass : function(){\r
44178         if(this.useSetClass){\r
44179             if(!Ext.isEmpty(this.oldCls)){\r
44180                 this.el.removeClass([this.oldCls, 'x-btn-pressed']);\r
44181             }\r
44182             this.oldCls = (this.iconCls || this.icon) ? (this.text ? ' x-btn-text-icon' : ' x-btn-icon') : ' x-btn-noicon';\r
44183             this.el.addClass([this.oldCls, this.pressed ? 'x-btn-pressed' : null]);\r
44184         }\r
44185     },\r
44186 \r
44187     // protected\r
44188     getMenuClass : function(){\r
44189         return this.menu ? (this.arrowAlign != 'bottom' ? 'x-btn-arrow' : 'x-btn-arrow-bottom') : '';\r
44190     },\r
44191 \r
44192     // private\r
44193     onRender : function(ct, position){\r
44194         if(!this.template){\r
44195             if(!Ext.Button.buttonTemplate){\r
44196                 // hideous table template\r
44197                 Ext.Button.buttonTemplate = new Ext.Template(\r
44198                     '<table id="{4}" cellspacing="0" class="x-btn {3}"><tbody class="{1}">',\r
44199                     '<tr><td class="x-btn-tl"><i>&#160;</i></td><td class="x-btn-tc"></td><td class="x-btn-tr"><i>&#160;</i></td></tr>',\r
44200                     '<tr><td class="x-btn-ml"><i>&#160;</i></td><td class="x-btn-mc"><em class="{2}" unselectable="on"><button type="{0}"></button></em></td><td class="x-btn-mr"><i>&#160;</i></td></tr>',\r
44201                     '<tr><td class="x-btn-bl"><i>&#160;</i></td><td class="x-btn-bc"></td><td class="x-btn-br"><i>&#160;</i></td></tr>',\r
44202                     '</tbody></table>');\r
44203                 Ext.Button.buttonTemplate.compile();\r
44204             }\r
44205             this.template = Ext.Button.buttonTemplate;\r
44206         }\r
44207 \r
44208         var btn, targs = this.getTemplateArgs();\r
44209 \r
44210         if(position){\r
44211             btn = this.template.insertBefore(position, targs, true);\r
44212         }else{\r
44213             btn = this.template.append(ct, targs, true);\r
44214         }\r
44215         /**\r
44216          * An {@link Ext.Element Element} encapsulating the Button's clickable element. By default,\r
44217          * this references a <tt>&lt;button&gt;</tt> element. Read only.\r
44218          * @type Ext.Element\r
44219          * @property btnEl\r
44220          */\r
44221         this.btnEl = btn.child(this.buttonSelector);\r
44222         this.mon(this.btnEl, {\r
44223             scope: this,\r
44224             focus: this.onFocus,\r
44225             blur: this.onBlur\r
44226         });\r
44227 \r
44228         this.initButtonEl(btn, this.btnEl);\r
44229 \r
44230         Ext.ButtonToggleMgr.register(this);\r
44231     },\r
44232 \r
44233     // private\r
44234     initButtonEl : function(btn, btnEl){\r
44235         this.el = btn;\r
44236         this.setIcon(this.icon);\r
44237         this.setText(this.text);\r
44238         this.setIconClass(this.iconCls);\r
44239         if(Ext.isDefined(this.tabIndex)){\r
44240             btnEl.dom.tabIndex = this.tabIndex;\r
44241         }\r
44242         if(this.tooltip){\r
44243             this.setTooltip(this.tooltip, true);\r
44244         }\r
44245 \r
44246         if(this.handleMouseEvents){\r
44247             this.mon(btn, {\r
44248                 scope: this,\r
44249                 mouseover: this.onMouseOver,\r
44250                 mousedown: this.onMouseDown\r
44251             });\r
44252 \r
44253             // new functionality for monitoring on the document level\r
44254             //this.mon(btn, 'mouseout', this.onMouseOut, this);\r
44255         }\r
44256 \r
44257         if(this.menu){\r
44258             this.mon(this.menu, {\r
44259                 scope: this,\r
44260                 show: this.onMenuShow,\r
44261                 hide: this.onMenuHide\r
44262             });\r
44263         }\r
44264 \r
44265         if(this.repeat){\r
44266             var repeater = new Ext.util.ClickRepeater(btn, Ext.isObject(this.repeat) ? this.repeat : {});\r
44267             this.mon(repeater, 'click', this.onClick, this);\r
44268         }\r
44269         this.mon(btn, this.clickEvent, this.onClick, this);\r
44270     },\r
44271 \r
44272     // private\r
44273     afterRender : function(){\r
44274         Ext.Button.superclass.afterRender.call(this);\r
44275         this.useSetClass = true;\r
44276         this.setButtonClass();\r
44277         this.doc = Ext.getDoc();\r
44278         this.doAutoWidth();\r
44279     },\r
44280 \r
44281     /**\r
44282      * Sets the CSS class that provides a background image to use as the button's icon.  This method also changes\r
44283      * the value of the {@link iconCls} config internally.\r
44284      * @param {String} cls The CSS class providing the icon image\r
44285      * @return {Ext.Button} this\r
44286      */\r
44287     setIconClass : function(cls){\r
44288         this.iconCls = cls;\r
44289         if(this.el){\r
44290             this.btnEl.dom.className = '';\r
44291             this.btnEl.addClass(['x-btn-text', cls || '']);\r
44292             this.setButtonClass();\r
44293         }\r
44294         return this;\r
44295     },\r
44296 \r
44297     /**\r
44298      * Sets the tooltip for this Button.\r
44299      * @param {String/Object} tooltip. This may be:<div class="mdesc-details"><ul>\r
44300      * <li><b>String</b> : A string to be used as innerHTML (html tags are accepted) to show in a tooltip</li>\r
44301      * <li><b>Object</b> : A configuration object for {@link Ext.QuickTips#register}.</li>\r
44302      * </ul></div>\r
44303      * @return {Ext.Button} this\r
44304      */\r
44305     setTooltip : function(tooltip, /* private */ initial){\r
44306         if(this.rendered){\r
44307             if(!initial){\r
44308                 this.clearTip();\r
44309             }\r
44310             if(Ext.isObject(tooltip)){\r
44311                 Ext.QuickTips.register(Ext.apply({\r
44312                       target: this.btnEl.id\r
44313                 }, tooltip));\r
44314                 this.tooltip = tooltip;\r
44315             }else{\r
44316                 this.btnEl.dom[this.tooltipType] = tooltip;\r
44317             }\r
44318         }else{\r
44319             this.tooltip = tooltip;\r
44320         }\r
44321         return this;\r
44322     },\r
44323 \r
44324     // private\r
44325     clearTip : function(){\r
44326         if(Ext.isObject(this.tooltip)){\r
44327             Ext.QuickTips.unregister(this.btnEl);\r
44328         }\r
44329     },\r
44330 \r
44331     // private\r
44332     beforeDestroy : function(){\r
44333         if(this.rendered){\r
44334             this.clearTip();\r
44335         }\r
44336         if(this.menu && this.destroyMenu !== false) {\r
44337             Ext.destroy(this.menu);\r
44338         }\r
44339         Ext.destroy(this.repeater);\r
44340     },\r
44341 \r
44342     // private\r
44343     onDestroy : function(){\r
44344         if(this.rendered){\r
44345             this.doc.un('mouseover', this.monitorMouseOver, this);\r
44346             this.doc.un('mouseup', this.onMouseUp, this);\r
44347             delete this.doc;\r
44348             delete this.btnEl;\r
44349             Ext.ButtonToggleMgr.unregister(this);\r
44350         }\r
44351         Ext.Button.superclass.onDestroy.call(this);\r
44352     },\r
44353 \r
44354     // private\r
44355     doAutoWidth : function(){\r
44356         if(this.el && this.text && this.width === undefined){\r
44357             this.el.setWidth('auto');\r
44358             if(Ext.isIE7 && Ext.isStrict){\r
44359                 var ib = this.btnEl;\r
44360                 if(ib && ib.getWidth() > 20){\r
44361                     ib.clip();\r
44362                     ib.setWidth(Ext.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));\r
44363                 }\r
44364             }\r
44365             if(this.minWidth){\r
44366                 if(this.el.getWidth() < this.minWidth){\r
44367                     this.el.setWidth(this.minWidth);\r
44368                 }\r
44369             }\r
44370         }\r
44371     },\r
44372 \r
44373     /**\r
44374      * Assigns this Button's click handler\r
44375      * @param {Function} handler The function to call when the button is clicked\r
44376      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the handler function is executed.\r
44377      * Defaults to this Button.\r
44378      * @return {Ext.Button} this\r
44379      */\r
44380     setHandler : function(handler, scope){\r
44381         this.handler = handler;\r
44382         this.scope = scope;\r
44383         return this;\r
44384     },\r
44385 \r
44386     /**\r
44387      * Sets this Button's text\r
44388      * @param {String} text The button text\r
44389      * @return {Ext.Button} this\r
44390      */\r
44391     setText : function(text){\r
44392         this.text = text;\r
44393         if(this.el){\r
44394             this.btnEl.update(text || '&#160;');\r
44395             this.setButtonClass();\r
44396         }\r
44397         this.doAutoWidth();\r
44398         return this;\r
44399     },\r
44400 \r
44401     /**\r
44402      * Sets the background image (inline style) of the button.  This method also changes\r
44403      * the value of the {@link icon} config internally.\r
44404      * @param {String} icon The path to an image to display in the button\r
44405      * @return {Ext.Button} this\r
44406      */\r
44407     setIcon : function(icon){\r
44408         this.icon = icon;\r
44409         if(this.el){\r
44410             this.btnEl.setStyle('background-image', icon ? 'url(' + icon + ')' : '');\r
44411             this.setButtonClass();\r
44412         }\r
44413         return this;\r
44414     },\r
44415 \r
44416     /**\r
44417      * Gets the text for this Button\r
44418      * @return {String} The button text\r
44419      */\r
44420     getText : function(){\r
44421         return this.text;\r
44422     },\r
44423 \r
44424     /**\r
44425      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.\r
44426      * @param {Boolean} state (optional) Force a particular state\r
44427      * @param {Boolean} supressEvent (optional) True to stop events being fired when calling this method.\r
44428      * @return {Ext.Button} this\r
44429      */\r
44430     toggle : function(state, suppressEvent){\r
44431         state = state === undefined ? !this.pressed : !!state;\r
44432         if(state != this.pressed){\r
44433             if(this.rendered){\r
44434                 this.el[state ? 'addClass' : 'removeClass']('x-btn-pressed');\r
44435             }\r
44436             this.pressed = state;\r
44437             if(!suppressEvent){\r
44438                 this.fireEvent('toggle', this, state);\r
44439                 if(this.toggleHandler){\r
44440                     this.toggleHandler.call(this.scope || this, this, state);\r
44441                 }\r
44442             }\r
44443         }\r
44444         return this;\r
44445     },\r
44446 \r
44447     /**\r
44448      * Focus the button\r
44449      */\r
44450     focus : function(){\r
44451         this.btnEl.focus();\r
44452     },\r
44453 \r
44454     // private\r
44455     onDisable : function(){\r
44456         this.onDisableChange(true);\r
44457     },\r
44458 \r
44459     // private\r
44460     onEnable : function(){\r
44461         this.onDisableChange(false);\r
44462     },\r
44463 \r
44464     onDisableChange : function(disabled){\r
44465         if(this.el){\r
44466             if(!Ext.isIE6 || !this.text){\r
44467                 this.el[disabled ? 'addClass' : 'removeClass'](this.disabledClass);\r
44468             }\r
44469             this.el.dom.disabled = disabled;\r
44470         }\r
44471         this.disabled = disabled;\r
44472     },\r
44473 \r
44474     /**\r
44475      * Show this button's menu (if it has one)\r
44476      */\r
44477     showMenu : function(){\r
44478         if(this.rendered && this.menu){\r
44479             if(this.tooltip){\r
44480                 Ext.QuickTips.getQuickTip().cancelShow(this.btnEl);\r
44481             }\r
44482             if(this.menu.isVisible()){\r
44483                 this.menu.hide();\r
44484             }\r
44485             this.menu.ownerCt = this;\r
44486             this.menu.show(this.el, this.menuAlign);\r
44487         }\r
44488         return this;\r
44489     },\r
44490 \r
44491     /**\r
44492      * Hide this button's menu (if it has one)\r
44493      */\r
44494     hideMenu : function(){\r
44495         if(this.hasVisibleMenu()){\r
44496             this.menu.hide();\r
44497         }\r
44498         return this;\r
44499     },\r
44500 \r
44501     /**\r
44502      * Returns true if the button has a menu and it is visible\r
44503      * @return {Boolean}\r
44504      */\r
44505     hasVisibleMenu : function(){\r
44506         return this.menu && this.menu.ownerCt == this && this.menu.isVisible();\r
44507     },\r
44508 \r
44509     // private\r
44510     onClick : function(e){\r
44511         if(e){\r
44512             e.preventDefault();\r
44513         }\r
44514         if(e.button !== 0){\r
44515             return;\r
44516         }\r
44517         if(!this.disabled){\r
44518             if(this.enableToggle && (this.allowDepress !== false || !this.pressed)){\r
44519                 this.toggle();\r
44520             }\r
44521             if(this.menu && !this.hasVisibleMenu() && !this.ignoreNextClick){\r
44522                 this.showMenu();\r
44523             }\r
44524             this.fireEvent('click', this, e);\r
44525             if(this.handler){\r
44526                 //this.el.removeClass('x-btn-over');\r
44527                 this.handler.call(this.scope || this, this, e);\r
44528             }\r
44529         }\r
44530     },\r
44531 \r
44532     // private\r
44533     isMenuTriggerOver : function(e, internal){\r
44534         return this.menu && !internal;\r
44535     },\r
44536 \r
44537     // private\r
44538     isMenuTriggerOut : function(e, internal){\r
44539         return this.menu && !internal;\r
44540     },\r
44541 \r
44542     // private\r
44543     onMouseOver : function(e){\r
44544         if(!this.disabled){\r
44545             var internal = e.within(this.el,  true);\r
44546             if(!internal){\r
44547                 this.el.addClass('x-btn-over');\r
44548                 if(!this.monitoringMouseOver){\r
44549                     this.doc.on('mouseover', this.monitorMouseOver, this);\r
44550                     this.monitoringMouseOver = true;\r
44551                 }\r
44552                 this.fireEvent('mouseover', this, e);\r
44553             }\r
44554             if(this.isMenuTriggerOver(e, internal)){\r
44555                 this.fireEvent('menutriggerover', this, this.menu, e);\r
44556             }\r
44557         }\r
44558     },\r
44559 \r
44560     // private\r
44561     monitorMouseOver : function(e){\r
44562         if(e.target != this.el.dom && !e.within(this.el)){\r
44563             if(this.monitoringMouseOver){\r
44564                 this.doc.un('mouseover', this.monitorMouseOver, this);\r
44565                 this.monitoringMouseOver = false;\r
44566             }\r
44567             this.onMouseOut(e);\r
44568         }\r
44569     },\r
44570 \r
44571     // private\r
44572     onMouseOut : function(e){\r
44573         var internal = e.within(this.el) && e.target != this.el.dom;\r
44574         this.el.removeClass('x-btn-over');\r
44575         this.fireEvent('mouseout', this, e);\r
44576         if(this.isMenuTriggerOut(e, internal)){\r
44577             this.fireEvent('menutriggerout', this, this.menu, e);\r
44578         }\r
44579     },\r
44580 \r
44581     focus : function() {\r
44582         this.btnEl.focus();\r
44583     },\r
44584 \r
44585     blur : function() {\r
44586         this.btnEl.blur();\r
44587     },\r
44588 \r
44589     // private\r
44590     onFocus : function(e){\r
44591         if(!this.disabled){\r
44592             this.el.addClass('x-btn-focus');\r
44593         }\r
44594     },\r
44595     // private\r
44596     onBlur : function(e){\r
44597         this.el.removeClass('x-btn-focus');\r
44598     },\r
44599 \r
44600     // private\r
44601     getClickEl : function(e, isUp){\r
44602        return this.el;\r
44603     },\r
44604 \r
44605     // private\r
44606     onMouseDown : function(e){\r
44607         if(!this.disabled && e.button === 0){\r
44608             this.getClickEl(e).addClass('x-btn-click');\r
44609             this.doc.on('mouseup', this.onMouseUp, this);\r
44610         }\r
44611     },\r
44612     // private\r
44613     onMouseUp : function(e){\r
44614         if(e.button === 0){\r
44615             this.getClickEl(e, true).removeClass('x-btn-click');\r
44616             this.doc.un('mouseup', this.onMouseUp, this);\r
44617         }\r
44618     },\r
44619     // private\r
44620     onMenuShow : function(e){\r
44621         if(this.menu.ownerCt == this){\r
44622             this.menu.ownerCt = this;\r
44623             this.ignoreNextClick = 0;\r
44624             this.el.addClass('x-btn-menu-active');\r
44625             this.fireEvent('menushow', this, this.menu);\r
44626         }\r
44627     },\r
44628     // private\r
44629     onMenuHide : function(e){\r
44630         if(this.menu.ownerCt == this){\r
44631             this.el.removeClass('x-btn-menu-active');\r
44632             this.ignoreNextClick = this.restoreClick.defer(250, this);\r
44633             this.fireEvent('menuhide', this, this.menu);\r
44634             delete this.menu.ownerCt;\r
44635         }\r
44636     },\r
44637 \r
44638     // private\r
44639     restoreClick : function(){\r
44640         this.ignoreNextClick = 0;\r
44641     }\r
44642 \r
44643     /**\r
44644      * @cfg {String} autoEl @hide\r
44645      */\r
44646     /**\r
44647      * @cfg {String/Object} html @hide\r
44648      */\r
44649     /**\r
44650      * @cfg {String} contentEl  @hide\r
44651      */\r
44652     /**\r
44653      * @cfg {Mixed} data  @hide\r
44654      */\r
44655     /**\r
44656      * @cfg {Mixed} tpl  @hide\r
44657      */\r
44658     /**\r
44659      * @cfg {String} tplWriteMode  @hide\r
44660      */\r
44661 });\r
44662 Ext.reg('button', Ext.Button);\r
44663 \r
44664 // Private utility class used by Button\r
44665 Ext.ButtonToggleMgr = function(){\r
44666    var groups = {};\r
44667 \r
44668    function toggleGroup(btn, state){\r
44669        if(state){\r
44670            var g = groups[btn.toggleGroup];\r
44671            for(var i = 0, l = g.length; i < l; i++){\r
44672                if(g[i] != btn){\r
44673                    g[i].toggle(false);\r
44674                }\r
44675            }\r
44676        }\r
44677    }\r
44678 \r
44679    return {\r
44680        register : function(btn){\r
44681            if(!btn.toggleGroup){\r
44682                return;\r
44683            }\r
44684            var g = groups[btn.toggleGroup];\r
44685            if(!g){\r
44686                g = groups[btn.toggleGroup] = [];\r
44687            }\r
44688            g.push(btn);\r
44689            btn.on('toggle', toggleGroup);\r
44690        },\r
44691 \r
44692        unregister : function(btn){\r
44693            if(!btn.toggleGroup){\r
44694                return;\r
44695            }\r
44696            var g = groups[btn.toggleGroup];\r
44697            if(g){\r
44698                g.remove(btn);\r
44699                btn.un('toggle', toggleGroup);\r
44700            }\r
44701        },\r
44702 \r
44703        /**\r
44704         * Gets the pressed button in the passed group or null\r
44705         * @param {String} group\r
44706         * @return Button\r
44707         */\r
44708        getPressed : function(group){\r
44709            var g = groups[group];\r
44710            if(g){\r
44711                for(var i = 0, len = g.length; i < len; i++){\r
44712                    if(g[i].pressed === true){\r
44713                        return g[i];\r
44714                    }\r
44715                }\r
44716            }\r
44717            return null;\r
44718        }\r
44719    };\r
44720 }();\r
44721 /**\r
44722  * @class Ext.SplitButton\r
44723  * @extends Ext.Button\r
44724  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default\r
44725  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional\r
44726  * options to the primary button action, but any custom handler can provide the arrowclick implementation.  Example usage:\r
44727  * <pre><code>\r
44728 // display a dropdown menu:\r
44729 new Ext.SplitButton({\r
44730         renderTo: 'button-ct', // the container id\r
44731         text: 'Options',\r
44732         handler: optionsHandler, // handle a click on the button itself\r
44733         menu: new Ext.menu.Menu({\r
44734         items: [\r
44735                 // these items will render as dropdown menu items when the arrow is clicked:\r
44736                 {text: 'Item 1', handler: item1Handler},\r
44737                 {text: 'Item 2', handler: item2Handler}\r
44738         ]\r
44739         })\r
44740 });\r
44741 \r
44742 // Instead of showing a menu, you provide any type of custom\r
44743 // functionality you want when the dropdown arrow is clicked:\r
44744 new Ext.SplitButton({\r
44745         renderTo: 'button-ct',\r
44746         text: 'Options',\r
44747         handler: optionsHandler,\r
44748         arrowHandler: myCustomHandler\r
44749 });\r
44750 </code></pre>\r
44751  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)\r
44752  * @cfg {String} arrowTooltip The title attribute of the arrow\r
44753  * @constructor\r
44754  * Create a new menu button\r
44755  * @param {Object} config The config object\r
44756  * @xtype splitbutton\r
44757  */\r
44758 Ext.SplitButton = Ext.extend(Ext.Button, {\r
44759         // private\r
44760     arrowSelector : 'em',\r
44761     split: true,\r
44762 \r
44763     // private\r
44764     initComponent : function(){\r
44765         Ext.SplitButton.superclass.initComponent.call(this);\r
44766         /**\r
44767          * @event arrowclick\r
44768          * Fires when this button's arrow is clicked\r
44769          * @param {MenuButton} this\r
44770          * @param {EventObject} e The click event\r
44771          */\r
44772         this.addEvents("arrowclick");\r
44773     },\r
44774 \r
44775     // private\r
44776     onRender : function(){\r
44777         Ext.SplitButton.superclass.onRender.apply(this, arguments);\r
44778         if(this.arrowTooltip){\r
44779             this.el.child(this.arrowSelector).dom[this.tooltipType] = this.arrowTooltip;\r
44780         }\r
44781     },\r
44782 \r
44783     /**\r
44784      * Sets this button's arrow click handler.\r
44785      * @param {Function} handler The function to call when the arrow is clicked\r
44786      * @param {Object} scope (optional) Scope for the function passed above\r
44787      */\r
44788     setArrowHandler : function(handler, scope){\r
44789         this.arrowHandler = handler;\r
44790         this.scope = scope;\r
44791     },\r
44792 \r
44793     getMenuClass : function(){\r
44794         return 'x-btn-split' + (this.arrowAlign == 'bottom' ? '-bottom' : '');\r
44795     },\r
44796 \r
44797     isClickOnArrow : function(e){\r
44798         if (this.arrowAlign != 'bottom') {\r
44799             var visBtn = this.el.child('em.x-btn-split');\r
44800             var right = visBtn.getRegion().right - visBtn.getPadding('r');\r
44801             return e.getPageX() > right;\r
44802         } else {\r
44803             return e.getPageY() > this.btnEl.getRegion().bottom;\r
44804         }\r
44805     },\r
44806 \r
44807     // private\r
44808     onClick : function(e, t){\r
44809         e.preventDefault();\r
44810         if(!this.disabled){\r
44811             if(this.isClickOnArrow(e)){\r
44812                 if(this.menu && !this.menu.isVisible() && !this.ignoreNextClick){\r
44813                     this.showMenu();\r
44814                 }\r
44815                 this.fireEvent("arrowclick", this, e);\r
44816                 if(this.arrowHandler){\r
44817                     this.arrowHandler.call(this.scope || this, this, e);\r
44818                 }\r
44819             }else{\r
44820                 if(this.enableToggle){\r
44821                     this.toggle();\r
44822                 }\r
44823                 this.fireEvent("click", this, e);\r
44824                 if(this.handler){\r
44825                     this.handler.call(this.scope || this, this, e);\r
44826                 }\r
44827             }\r
44828         }\r
44829     },\r
44830 \r
44831     // private\r
44832     isMenuTriggerOver : function(e){\r
44833         return this.menu && e.target.tagName == this.arrowSelector;\r
44834     },\r
44835 \r
44836     // private\r
44837     isMenuTriggerOut : function(e, internal){\r
44838         return this.menu && e.target.tagName != this.arrowSelector;\r
44839     }\r
44840 });\r
44841 \r
44842 Ext.reg('splitbutton', Ext.SplitButton);/**\r
44843  * @class Ext.CycleButton\r
44844  * @extends Ext.SplitButton\r
44845  * A specialized SplitButton that contains a menu of {@link Ext.menu.CheckItem} elements.  The button automatically\r
44846  * cycles through each menu item on click, raising the button's {@link #change} event (or calling the button's\r
44847  * {@link #changeHandler} function, if supplied) for the active menu item. Clicking on the arrow section of the\r
44848  * button displays the dropdown menu just like a normal SplitButton.  Example usage:\r
44849  * <pre><code>\r
44850 var btn = new Ext.CycleButton({\r
44851     showText: true,\r
44852     prependText: 'View as ',\r
44853     items: [{\r
44854         text:'text only',\r
44855         iconCls:'view-text',\r
44856         checked:true\r
44857     },{\r
44858         text:'HTML',\r
44859         iconCls:'view-html'\r
44860     }],\r
44861     changeHandler:function(btn, item){\r
44862         Ext.Msg.alert('Change View', item.text);\r
44863     }\r
44864 });\r
44865 </code></pre>\r
44866  * @constructor\r
44867  * Create a new split button\r
44868  * @param {Object} config The config object\r
44869  * @xtype cycle\r
44870  */\r
44871 Ext.CycleButton = Ext.extend(Ext.SplitButton, {\r
44872     /**\r
44873      * @cfg {Array} items An array of {@link Ext.menu.CheckItem} <b>config</b> objects to be used when creating the\r
44874      * button's menu items (e.g., {text:'Foo', iconCls:'foo-icon'})\r
44875      */\r
44876     /**\r
44877      * @cfg {Boolean} showText True to display the active item's text as the button text (defaults to false)\r
44878      */\r
44879     /**\r
44880      * @cfg {String} prependText A static string to prepend before the active item's text when displayed as the\r
44881      * button's text (only applies when showText = true, defaults to '')\r
44882      */\r
44883     /**\r
44884      * @cfg {Function} changeHandler A callback function that will be invoked each time the active menu\r
44885      * item in the button's menu has changed.  If this callback is not supplied, the SplitButton will instead\r
44886      * fire the {@link #change} event on active item change.  The changeHandler function will be called with the\r
44887      * following argument list: (SplitButton this, Ext.menu.CheckItem item)\r
44888      */\r
44889     /**\r
44890      * @cfg {String} forceIcon A css class which sets an image to be used as the static icon for this button.  This\r
44891      * icon will always be displayed regardless of which item is selected in the dropdown list.  This overrides the \r
44892      * default behavior of changing the button's icon to match the selected item's icon on change.\r
44893      */\r
44894     /**\r
44895      * @property menu\r
44896      * @type Menu\r
44897      * The {@link Ext.menu.Menu Menu} object used to display the {@link Ext.menu.CheckItem CheckItems} representing the available choices.\r
44898      */\r
44899 \r
44900     // private\r
44901     getItemText : function(item){\r
44902         if(item && this.showText === true){\r
44903             var text = '';\r
44904             if(this.prependText){\r
44905                 text += this.prependText;\r
44906             }\r
44907             text += item.text;\r
44908             return text;\r
44909         }\r
44910         return undefined;\r
44911     },\r
44912 \r
44913     /**\r
44914      * Sets the button's active menu item.\r
44915      * @param {Ext.menu.CheckItem} item The item to activate\r
44916      * @param {Boolean} suppressEvent True to prevent the button's change event from firing (defaults to false)\r
44917      */\r
44918     setActiveItem : function(item, suppressEvent){\r
44919         if(!Ext.isObject(item)){\r
44920             item = this.menu.getComponent(item);\r
44921         }\r
44922         if(item){\r
44923             if(!this.rendered){\r
44924                 this.text = this.getItemText(item);\r
44925                 this.iconCls = item.iconCls;\r
44926             }else{\r
44927                 var t = this.getItemText(item);\r
44928                 if(t){\r
44929                     this.setText(t);\r
44930                 }\r
44931                 this.setIconClass(item.iconCls);\r
44932             }\r
44933             this.activeItem = item;\r
44934             if(!item.checked){\r
44935                 item.setChecked(true, false);\r
44936             }\r
44937             if(this.forceIcon){\r
44938                 this.setIconClass(this.forceIcon);\r
44939             }\r
44940             if(!suppressEvent){\r
44941                 this.fireEvent('change', this, item);\r
44942             }\r
44943         }\r
44944     },\r
44945 \r
44946     /**\r
44947      * Gets the currently active menu item.\r
44948      * @return {Ext.menu.CheckItem} The active item\r
44949      */\r
44950     getActiveItem : function(){\r
44951         return this.activeItem;\r
44952     },\r
44953 \r
44954     // private\r
44955     initComponent : function(){\r
44956         this.addEvents(\r
44957             /**\r
44958              * @event change\r
44959              * Fires after the button's active menu item has changed.  Note that if a {@link #changeHandler} function\r
44960              * is set on this CycleButton, it will be called instead on active item change and this change event will\r
44961              * not be fired.\r
44962              * @param {Ext.CycleButton} this\r
44963              * @param {Ext.menu.CheckItem} item The menu item that was selected\r
44964              */\r
44965             "change"\r
44966         );\r
44967 \r
44968         if(this.changeHandler){\r
44969             this.on('change', this.changeHandler, this.scope||this);\r
44970             delete this.changeHandler;\r
44971         }\r
44972 \r
44973         this.itemCount = this.items.length;\r
44974 \r
44975         this.menu = {cls:'x-cycle-menu', items:[]};\r
44976         var checked = 0;\r
44977         Ext.each(this.items, function(item, i){\r
44978             Ext.apply(item, {\r
44979                 group: item.group || this.id,\r
44980                 itemIndex: i,\r
44981                 checkHandler: this.checkHandler,\r
44982                 scope: this,\r
44983                 checked: item.checked || false\r
44984             });\r
44985             this.menu.items.push(item);\r
44986             if(item.checked){\r
44987                 checked = i;\r
44988             }\r
44989         }, this);\r
44990         Ext.CycleButton.superclass.initComponent.call(this);\r
44991         this.on('click', this.toggleSelected, this);\r
44992         this.setActiveItem(checked, true);\r
44993     },\r
44994 \r
44995     // private\r
44996     checkHandler : function(item, pressed){\r
44997         if(pressed){\r
44998             this.setActiveItem(item);\r
44999         }\r
45000     },\r
45001 \r
45002     /**\r
45003      * This is normally called internally on button click, but can be called externally to advance the button's\r
45004      * active item programmatically to the next one in the menu.  If the current item is the last one in the menu\r
45005      * the active item will be set to the first item in the menu.\r
45006      */\r
45007     toggleSelected : function(){\r
45008         var m = this.menu;\r
45009         m.render();\r
45010         // layout if we haven't before so the items are active\r
45011         if(!m.hasLayout){\r
45012             m.doLayout();\r
45013         }\r
45014         \r
45015         var nextIdx, checkItem;\r
45016         for (var i = 1; i < this.itemCount; i++) {\r
45017             nextIdx = (this.activeItem.itemIndex + i) % this.itemCount;\r
45018             // check the potential item\r
45019             checkItem = m.items.itemAt(nextIdx);\r
45020             // if its not disabled then check it.\r
45021             if (!checkItem.disabled) {\r
45022                 checkItem.setChecked(true);\r
45023                 break;\r
45024             }\r
45025         }\r
45026     }\r
45027 });\r
45028 Ext.reg('cycle', Ext.CycleButton);/**
45029  * @class Ext.Toolbar
45030  * @extends Ext.Container
45031  * <p>Basic Toolbar class. Although the <tt>{@link Ext.Container#defaultType defaultType}</tt> for Toolbar
45032  * is <tt>{@link Ext.Button button}</tt>, Toolbar elements (child items for the Toolbar container) may
45033  * be virtually any type of Component. Toolbar elements can be created explicitly via their constructors,
45034  * or implicitly via their xtypes, and can be <tt>{@link #add}</tt>ed dynamically.</p>
45035  * <p>Some items have shortcut strings for creation:</p>
45036  * <pre>
45037 <u>Shortcut</u>  <u>xtype</u>          <u>Class</u>                  <u>Description</u>
45038 '->'      'tbfill'       {@link Ext.Toolbar.Fill}       begin using the right-justified button container
45039 '-'       'tbseparator'  {@link Ext.Toolbar.Separator}  add a vertical separator bar between toolbar items
45040 ' '       'tbspacer'     {@link Ext.Toolbar.Spacer}     add horiztonal space between elements
45041  * </pre>
45042  *
45043  * Example usage of various elements:
45044  * <pre><code>
45045 var tb = new Ext.Toolbar({
45046     renderTo: document.body,
45047     width: 600,
45048     height: 100,
45049     items: [
45050         {
45051             // xtype: 'button', // default for Toolbars, same as 'tbbutton'
45052             text: 'Button'
45053         },
45054         {
45055             xtype: 'splitbutton', // same as 'tbsplitbutton'
45056             text: 'Split Button'
45057         },
45058         // begin using the right-justified button container
45059         '->', // same as {xtype: 'tbfill'}, // Ext.Toolbar.Fill
45060         {
45061             xtype: 'textfield',
45062             name: 'field1',
45063             emptyText: 'enter search term'
45064         },
45065         // add a vertical separator bar between toolbar items
45066         '-', // same as {xtype: 'tbseparator'} to create Ext.Toolbar.Separator
45067         'text 1', // same as {xtype: 'tbtext', text: 'text1'} to create Ext.Toolbar.TextItem
45068         {xtype: 'tbspacer'},// same as ' ' to create Ext.Toolbar.Spacer
45069         'text 2',
45070         {xtype: 'tbspacer', width: 50}, // add a 50px space
45071         'text 3'
45072     ]
45073 });
45074  * </code></pre>
45075  * Example adding a ComboBox within a menu of a button:
45076  * <pre><code>
45077 // ComboBox creation
45078 var combo = new Ext.form.ComboBox({
45079     store: new Ext.data.ArrayStore({
45080         autoDestroy: true,
45081         fields: ['initials', 'fullname'],
45082         data : [
45083             ['FF', 'Fred Flintstone'],
45084             ['BR', 'Barney Rubble']
45085         ]
45086     }),
45087     displayField: 'fullname',
45088     typeAhead: true,
45089     mode: 'local',
45090     forceSelection: true,
45091     triggerAction: 'all',
45092     emptyText: 'Select a name...',
45093     selectOnFocus: true,
45094     width: 135,
45095     getListParent: function() {
45096         return this.el.up('.x-menu');
45097     },
45098     iconCls: 'no-icon' //use iconCls if placing within menu to shift to right side of menu
45099 });
45100
45101 // put ComboBox in a Menu
45102 var menu = new Ext.menu.Menu({
45103     id: 'mainMenu',
45104     items: [
45105         combo // A Field in a Menu
45106     ]
45107 });
45108
45109 // add a Button with the menu
45110 tb.add({
45111         text:'Button w/ Menu',
45112         menu: menu  // assign menu by instance
45113     });
45114 tb.doLayout();
45115  * </code></pre>
45116  * @constructor
45117  * Creates a new Toolbar
45118  * @param {Object/Array} config A config object or an array of buttons to <tt>{@link #add}</tt>
45119  * @xtype toolbar
45120  */
45121 Ext.Toolbar = function(config){
45122     if(Ext.isArray(config)){
45123         config = {items: config, layout: 'toolbar'};
45124     } else {
45125         config = Ext.apply({
45126             layout: 'toolbar'
45127         }, config);
45128         if(config.buttons) {
45129             config.items = config.buttons;
45130         }
45131     }
45132     Ext.Toolbar.superclass.constructor.call(this, config);
45133 };
45134
45135 (function(){
45136
45137 var T = Ext.Toolbar;
45138
45139 Ext.extend(T, Ext.Container, {
45140
45141     defaultType: 'button',
45142
45143     /**
45144      * @cfg {String/Object} layout
45145      * This class assigns a default layout (<code>layout:'<b>toolbar</b>'</code>).
45146      * Developers <i>may</i> override this configuration option if another layout
45147      * is required (the constructor must be passed a configuration object in this
45148      * case instead of an array).
45149      * See {@link Ext.Container#layout} for additional information.
45150      */
45151
45152     enableOverflow : false,
45153
45154     /**
45155      * @cfg {Boolean} enableOverflow
45156      * Defaults to false. Configure <code>true<code> to make the toolbar provide a button
45157      * which activates a dropdown Menu to show items which overflow the Toolbar's width.
45158      */
45159     /**
45160      * @cfg {String} buttonAlign
45161      * <p>The default position at which to align child items. Defaults to <code>"left"</code></p>
45162      * <p>May be specified as <code>"center"</code> to cause items added before a Fill (A <code>"->"</code>) item
45163      * to be centered in the Toolbar. Items added after a Fill are still right-aligned.</p>
45164      * <p>Specify as <code>"right"</code> to right align all child items.</p>
45165      */
45166
45167     trackMenus : true,
45168     internalDefaults: {removeMode: 'container', hideParent: true},
45169     toolbarCls: 'x-toolbar',
45170
45171     initComponent : function(){
45172         T.superclass.initComponent.call(this);
45173
45174         /**
45175          * @event overflowchange
45176          * Fires after the overflow state has changed.
45177          * @param {Object} c The Container
45178          * @param {Boolean} lastOverflow overflow state
45179          */
45180         this.addEvents('overflowchange');
45181     },
45182
45183     // private
45184     onRender : function(ct, position){
45185         if(!this.el){
45186             if(!this.autoCreate){
45187                 this.autoCreate = {
45188                     cls: this.toolbarCls + ' x-small-editor'
45189                 };
45190             }
45191             this.el = ct.createChild(Ext.apply({ id: this.id },this.autoCreate), position);
45192             Ext.Toolbar.superclass.onRender.apply(this, arguments);
45193         }
45194     },
45195
45196     /**
45197      * <p>Adds element(s) to the toolbar -- this function takes a variable number of
45198      * arguments of mixed type and adds them to the toolbar.</p>
45199      * <br><p><b>Note</b>: See the notes within {@link Ext.Container#add}.</p>
45200      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
45201      * <ul>
45202      * <li>{@link Ext.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
45203      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
45204      * <li>Field: Any form field (equivalent to {@link #addField})</li>
45205      * <li>Item: Any subclass of {@link Ext.Toolbar.Item} (equivalent to {@link #addItem})</li>
45206      * <li>String: Any generic string (gets wrapped in a {@link Ext.Toolbar.TextItem}, equivalent to {@link #addText}).
45207      * Note that there are a few special strings that are treated differently as explained next.</li>
45208      * <li>'-': Creates a separator element (equivalent to {@link #addSeparator})</li>
45209      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
45210      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
45211      * </ul>
45212      * @param {Mixed} arg2
45213      * @param {Mixed} etc.
45214      * @method add
45215      */
45216
45217     // private
45218     lookupComponent : function(c){
45219         if(Ext.isString(c)){
45220             if(c == '-'){
45221                 c = new T.Separator();
45222             }else if(c == ' '){
45223                 c = new T.Spacer();
45224             }else if(c == '->'){
45225                 c = new T.Fill();
45226             }else{
45227                 c = new T.TextItem(c);
45228             }
45229             this.applyDefaults(c);
45230         }else{
45231             if(c.isFormField || c.render){ // some kind of form field, some kind of Toolbar.Item
45232                 c = this.createComponent(c);
45233             }else if(c.tag){ // DomHelper spec
45234                 c = new T.Item({autoEl: c});
45235             }else if(c.tagName){ // element
45236                 c = new T.Item({el:c});
45237             }else if(Ext.isObject(c)){ // must be button config?
45238                 c = c.xtype ? this.createComponent(c) : this.constructButton(c);
45239             }
45240         }
45241         return c;
45242     },
45243
45244     // private
45245     applyDefaults : function(c){
45246         if(!Ext.isString(c)){
45247             c = Ext.Toolbar.superclass.applyDefaults.call(this, c);
45248             var d = this.internalDefaults;
45249             if(c.events){
45250                 Ext.applyIf(c.initialConfig, d);
45251                 Ext.apply(c, d);
45252             }else{
45253                 Ext.applyIf(c, d);
45254             }
45255         }
45256         return c;
45257     },
45258
45259     /**
45260      * Adds a separator
45261      * <br><p><b>Note</b>: See the notes within {@link Ext.Container#add}.</p>
45262      * @return {Ext.Toolbar.Item} The separator {@link Ext.Toolbar.Item item}
45263      */
45264     addSeparator : function(){
45265         return this.add(new T.Separator());
45266     },
45267
45268     /**
45269      * Adds a spacer element
45270      * <br><p><b>Note</b>: See the notes within {@link Ext.Container#add}.</p>
45271      * @return {Ext.Toolbar.Spacer} The spacer item
45272      */
45273     addSpacer : function(){
45274         return this.add(new T.Spacer());
45275     },
45276
45277     /**
45278      * Forces subsequent additions into the float:right toolbar
45279      * <br><p><b>Note</b>: See the notes within {@link Ext.Container#add}.</p>
45280      */
45281     addFill : function(){
45282         this.add(new T.Fill());
45283     },
45284
45285     /**
45286      * Adds any standard HTML element to the toolbar
45287      * <br><p><b>Note</b>: See the notes within {@link Ext.Container#add}.</p>
45288      * @param {Mixed} el The element or id of the element to add
45289      * @return {Ext.Toolbar.Item} The element's item
45290      */
45291     addElement : function(el){
45292         return this.addItem(new T.Item({el:el}));
45293     },
45294
45295     /**
45296      * Adds any Toolbar.Item or subclass
45297      * <br><p><b>Note</b>: See the notes within {@link Ext.Container#add}.</p>
45298      * @param {Ext.Toolbar.Item} item
45299      * @return {Ext.Toolbar.Item} The item
45300      */
45301     addItem : function(item){
45302         return this.add.apply(this, arguments);
45303     },
45304
45305     /**
45306      * Adds a button (or buttons). See {@link Ext.Button} for more info on the config.
45307      * <br><p><b>Note</b>: See the notes within {@link Ext.Container#add}.</p>
45308      * @param {Object/Array} config A button config or array of configs
45309      * @return {Ext.Button/Array}
45310      */
45311     addButton : function(config){
45312         if(Ext.isArray(config)){
45313             var buttons = [];
45314             for(var i = 0, len = config.length; i < len; i++) {
45315                 buttons.push(this.addButton(config[i]));
45316             }
45317             return buttons;
45318         }
45319         return this.add(this.constructButton(config));
45320     },
45321
45322     /**
45323      * Adds text to the toolbar
45324      * <br><p><b>Note</b>: See the notes within {@link Ext.Container#add}.</p>
45325      * @param {String} text The text to add
45326      * @return {Ext.Toolbar.Item} The element's item
45327      */
45328     addText : function(text){
45329         return this.addItem(new T.TextItem(text));
45330     },
45331
45332     /**
45333      * Adds a new element to the toolbar from the passed {@link Ext.DomHelper} config
45334      * <br><p><b>Note</b>: See the notes within {@link Ext.Container#add}.</p>
45335      * @param {Object} config
45336      * @return {Ext.Toolbar.Item} The element's item
45337      */
45338     addDom : function(config){
45339         return this.add(new T.Item({autoEl: config}));
45340     },
45341
45342     /**
45343      * Adds a dynamically rendered Ext.form field (TextField, ComboBox, etc). Note: the field should not have
45344      * been rendered yet. For a field that has already been rendered, use {@link #addElement}.
45345      * <br><p><b>Note</b>: See the notes within {@link Ext.Container#add}.</p>
45346      * @param {Ext.form.Field} field
45347      * @return {Ext.Toolbar.Item}
45348      */
45349     addField : function(field){
45350         return this.add(field);
45351     },
45352
45353     /**
45354      * Inserts any {@link Ext.Toolbar.Item}/{@link Ext.Button} at the specified index.
45355      * <br><p><b>Note</b>: See the notes within {@link Ext.Container#add}.</p>
45356      * @param {Number} index The index where the item is to be inserted
45357      * @param {Object/Ext.Toolbar.Item/Ext.Button/Array} item The button, or button config object to be
45358      * inserted, or an array of buttons/configs.
45359      * @return {Ext.Button/Item}
45360      */
45361     insertButton : function(index, item){
45362         if(Ext.isArray(item)){
45363             var buttons = [];
45364             for(var i = 0, len = item.length; i < len; i++) {
45365                buttons.push(this.insertButton(index + i, item[i]));
45366             }
45367             return buttons;
45368         }
45369         return Ext.Toolbar.superclass.insert.call(this, index, item);
45370     },
45371
45372     // private
45373     trackMenu : function(item, remove){
45374         if(this.trackMenus && item.menu){
45375             var method = remove ? 'mun' : 'mon';
45376             this[method](item, 'menutriggerover', this.onButtonTriggerOver, this);
45377             this[method](item, 'menushow', this.onButtonMenuShow, this);
45378             this[method](item, 'menuhide', this.onButtonMenuHide, this);
45379         }
45380     },
45381
45382     // private
45383     constructButton : function(item){
45384         var b = item.events ? item : this.createComponent(item, item.split ? 'splitbutton' : this.defaultType);
45385         return b;
45386     },
45387
45388     // private
45389     onAdd : function(c){
45390         Ext.Toolbar.superclass.onAdd.call(this);
45391         this.trackMenu(c);
45392         if(this.disabled){
45393             c.disable();
45394         }
45395     },
45396
45397     // private
45398     onRemove : function(c){
45399         Ext.Toolbar.superclass.onRemove.call(this);
45400         this.trackMenu(c, true);
45401     },
45402
45403     // private
45404     onDisable : function(){
45405         this.items.each(function(item){
45406              if(item.disable){
45407                  item.disable();
45408              }
45409         });
45410     },
45411
45412     // private
45413     onEnable : function(){
45414         this.items.each(function(item){
45415              if(item.enable){
45416                  item.enable();
45417              }
45418         });
45419     },
45420
45421     // private
45422     onButtonTriggerOver : function(btn){
45423         if(this.activeMenuBtn && this.activeMenuBtn != btn){
45424             this.activeMenuBtn.hideMenu();
45425             btn.showMenu();
45426             this.activeMenuBtn = btn;
45427         }
45428     },
45429
45430     // private
45431     onButtonMenuShow : function(btn){
45432         this.activeMenuBtn = btn;
45433     },
45434
45435     // private
45436     onButtonMenuHide : function(btn){
45437         delete this.activeMenuBtn;
45438     }
45439 });
45440 Ext.reg('toolbar', Ext.Toolbar);
45441
45442 /**
45443  * @class Ext.Toolbar.Item
45444  * @extends Ext.BoxComponent
45445  * The base class that other non-interacting Toolbar Item classes should extend in order to
45446  * get some basic common toolbar item functionality.
45447  * @constructor
45448  * Creates a new Item
45449  * @param {HTMLElement} el
45450  * @xtype tbitem
45451  */
45452 T.Item = Ext.extend(Ext.BoxComponent, {
45453     hideParent: true, //  Hiding a Toolbar.Item hides its containing TD
45454     enable:Ext.emptyFn,
45455     disable:Ext.emptyFn,
45456     focus:Ext.emptyFn
45457     /**
45458      * @cfg {String} overflowText Text to be used for the menu if the item is overflowed.
45459      */
45460 });
45461 Ext.reg('tbitem', T.Item);
45462
45463 /**
45464  * @class Ext.Toolbar.Separator
45465  * @extends Ext.Toolbar.Item
45466  * A simple class that adds a vertical separator bar between toolbar items
45467  * (css class:<tt>'xtb-sep'</tt>). Example usage:
45468  * <pre><code>
45469 new Ext.Panel({
45470     tbar : [
45471         'Item 1',
45472         {xtype: 'tbseparator'}, // or '-'
45473         'Item 2'
45474     ]
45475 });
45476 </code></pre>
45477  * @constructor
45478  * Creates a new Separator
45479  * @xtype tbseparator
45480  */
45481 T.Separator = Ext.extend(T.Item, {
45482     onRender : function(ct, position){
45483         this.el = ct.createChild({tag:'span', cls:'xtb-sep'}, position);
45484     }
45485 });
45486 Ext.reg('tbseparator', T.Separator);
45487
45488 /**
45489  * @class Ext.Toolbar.Spacer
45490  * @extends Ext.Toolbar.Item
45491  * A simple element that adds extra horizontal space between items in a toolbar.
45492  * By default a 2px wide space is added via css specification:<pre><code>
45493 .x-toolbar .xtb-spacer {
45494     width:2px;
45495 }
45496  * </code></pre>
45497  * <p>Example usage:</p>
45498  * <pre><code>
45499 new Ext.Panel({
45500     tbar : [
45501         'Item 1',
45502         {xtype: 'tbspacer'}, // or ' '
45503         'Item 2',
45504         // space width is also configurable via javascript
45505         {xtype: 'tbspacer', width: 50}, // add a 50px space
45506         'Item 3'
45507     ]
45508 });
45509 </code></pre>
45510  * @constructor
45511  * Creates a new Spacer
45512  * @xtype tbspacer
45513  */
45514 T.Spacer = Ext.extend(T.Item, {
45515     /**
45516      * @cfg {Number} width
45517      * The width of the spacer in pixels (defaults to 2px via css style <tt>.x-toolbar .xtb-spacer</tt>).
45518      */
45519
45520     onRender : function(ct, position){
45521         this.el = ct.createChild({tag:'div', cls:'xtb-spacer', style: this.width?'width:'+this.width+'px':''}, position);
45522     }
45523 });
45524 Ext.reg('tbspacer', T.Spacer);
45525
45526 /**
45527  * @class Ext.Toolbar.Fill
45528  * @extends Ext.Toolbar.Spacer
45529  * A non-rendering placeholder item which instructs the Toolbar's Layout to begin using
45530  * the right-justified button container.
45531  * <pre><code>
45532 new Ext.Panel({
45533     tbar : [
45534         'Item 1',
45535         {xtype: 'tbfill'}, // or '->'
45536         'Item 2'
45537     ]
45538 });
45539 </code></pre>
45540  * @constructor
45541  * Creates a new Fill
45542  * @xtype tbfill
45543  */
45544 T.Fill = Ext.extend(T.Item, {
45545     // private
45546     render : Ext.emptyFn,
45547     isFill : true
45548 });
45549 Ext.reg('tbfill', T.Fill);
45550
45551 /**
45552  * @class Ext.Toolbar.TextItem
45553  * @extends Ext.Toolbar.Item
45554  * A simple class that renders text directly into a toolbar
45555  * (with css class:<tt>'xtb-text'</tt>). Example usage:
45556  * <pre><code>
45557 new Ext.Panel({
45558     tbar : [
45559         {xtype: 'tbtext', text: 'Item 1'} // or simply 'Item 1'
45560     ]
45561 });
45562 </code></pre>
45563  * @constructor
45564  * Creates a new TextItem
45565  * @param {String/Object} text A text string, or a config object containing a <tt>text</tt> property
45566  * @xtype tbtext
45567  */
45568 T.TextItem = Ext.extend(T.Item, {
45569     /**
45570      * @cfg {String} text The text to be used as innerHTML (html tags are accepted)
45571      */
45572
45573     constructor: function(config){
45574         T.TextItem.superclass.constructor.call(this, Ext.isString(config) ? {text: config} : config);
45575     },
45576
45577     // private
45578     onRender : function(ct, position) {
45579         this.autoEl = {cls: 'xtb-text', html: this.text || ''};
45580         T.TextItem.superclass.onRender.call(this, ct, position);
45581     },
45582
45583     /**
45584      * Updates this item's text, setting the text to be used as innerHTML.
45585      * @param {String} t The text to display (html accepted).
45586      */
45587     setText : function(t) {
45588         if(this.rendered){
45589             this.el.update(t);
45590         }else{
45591             this.text = t;
45592         }
45593     }
45594 });
45595 Ext.reg('tbtext', T.TextItem);
45596
45597 // backwards compat
45598 T.Button = Ext.extend(Ext.Button, {});
45599 T.SplitButton = Ext.extend(Ext.SplitButton, {});
45600 Ext.reg('tbbutton', T.Button);
45601 Ext.reg('tbsplit', T.SplitButton);
45602
45603 })();
45604 /**\r
45605  * @class Ext.ButtonGroup\r
45606  * @extends Ext.Panel\r
45607  * Container for a group of buttons. Example usage:\r
45608  * <pre><code>\r
45609 var p = new Ext.Panel({\r
45610     title: 'Panel with Button Group',\r
45611     width: 300,\r
45612     height:200,\r
45613     renderTo: document.body,\r
45614     html: 'whatever',\r
45615     tbar: [{\r
45616         xtype: 'buttongroup',\r
45617         {@link #columns}: 3,\r
45618         title: 'Clipboard',\r
45619         items: [{\r
45620             text: 'Paste',\r
45621             scale: 'large',\r
45622             rowspan: 3, iconCls: 'add',\r
45623             iconAlign: 'top',\r
45624             cls: 'x-btn-as-arrow'\r
45625         },{\r
45626             xtype:'splitbutton',\r
45627             text: 'Menu Button',\r
45628             scale: 'large',\r
45629             rowspan: 3,\r
45630             iconCls: 'add',\r
45631             iconAlign: 'top',\r
45632             arrowAlign:'bottom',\r
45633             menu: [{text: 'Menu Item 1'}]\r
45634         },{\r
45635             xtype:'splitbutton', text: 'Cut', iconCls: 'add16', menu: [{text: 'Cut Menu Item'}]\r
45636         },{\r
45637             text: 'Copy', iconCls: 'add16'\r
45638         },{\r
45639             text: 'Format', iconCls: 'add16'\r
45640         }]\r
45641     }]\r
45642 });\r
45643  * </code></pre>\r
45644  * @constructor\r
45645  * Create a new ButtonGroup.\r
45646  * @param {Object} config The config object\r
45647  * @xtype buttongroup\r
45648  */\r
45649 Ext.ButtonGroup = Ext.extend(Ext.Panel, {\r
45650     /**\r
45651      * @cfg {Number} columns The <tt>columns</tt> configuration property passed to the\r
45652      * {@link #layout configured layout manager}. See {@link Ext.layout.TableLayout#columns}.\r
45653      */\r
45654     /**\r
45655      * @cfg {String} baseCls  Defaults to <tt>'x-btn-group'</tt>.  See {@link Ext.Panel#baseCls}.\r
45656      */\r
45657     baseCls: 'x-btn-group',\r
45658     /**\r
45659      * @cfg {String} layout  Defaults to <tt>'table'</tt>.  See {@link Ext.Container#layout}.\r
45660      */\r
45661     layout:'table',\r
45662     defaultType: 'button',\r
45663     /**\r
45664      * @cfg {Boolean} frame  Defaults to <tt>true</tt>.  See {@link Ext.Panel#frame}.\r
45665      */\r
45666     frame: true,\r
45667     internalDefaults: {removeMode: 'container', hideParent: true},\r
45668 \r
45669     initComponent : function(){\r
45670         this.layoutConfig = this.layoutConfig || {};\r
45671         Ext.applyIf(this.layoutConfig, {\r
45672             columns : this.columns\r
45673         });\r
45674         if(!this.title){\r
45675             this.addClass('x-btn-group-notitle');\r
45676         }\r
45677         this.on('afterlayout', this.onAfterLayout, this);\r
45678         Ext.ButtonGroup.superclass.initComponent.call(this);\r
45679     },\r
45680 \r
45681     applyDefaults : function(c){\r
45682         c = Ext.ButtonGroup.superclass.applyDefaults.call(this, c);\r
45683         var d = this.internalDefaults;\r
45684         if(c.events){\r
45685             Ext.applyIf(c.initialConfig, d);\r
45686             Ext.apply(c, d);\r
45687         }else{\r
45688             Ext.applyIf(c, d);\r
45689         }\r
45690         return c;\r
45691     },\r
45692 \r
45693     onAfterLayout : function(){\r
45694         var bodyWidth = this.body.getFrameWidth('lr') + this.body.dom.firstChild.offsetWidth;\r
45695         this.body.setWidth(bodyWidth);\r
45696         this.el.setWidth(bodyWidth + this.getFrameWidth());\r
45697     }\r
45698     /**\r
45699      * @cfg {Array} tools  @hide\r
45700      */\r
45701 });\r
45702 \r
45703 Ext.reg('buttongroup', Ext.ButtonGroup);\r
45704 /**
45705  * @class Ext.PagingToolbar
45706  * @extends Ext.Toolbar
45707  * <p>As the amount of records increases, the time required for the browser to render
45708  * them increases. Paging is used to reduce the amount of data exchanged with the client.
45709  * Note: if there are more records/rows than can be viewed in the available screen area, vertical
45710  * scrollbars will be added.</p>
45711  * <p>Paging is typically handled on the server side (see exception below). The client sends
45712  * parameters to the server side, which the server needs to interpret and then respond with the
45713  * approprate data.</p>
45714  * <p><b>Ext.PagingToolbar</b> is a specialized toolbar that is bound to a {@link Ext.data.Store}
45715  * and provides automatic paging control. This Component {@link Ext.data.Store#load load}s blocks
45716  * of data into the <tt>{@link #store}</tt> by passing {@link Ext.data.Store#paramNames paramNames} used for
45717  * paging criteria.</p>
45718  * <p>PagingToolbar is typically used as one of the Grid's toolbars:</p>
45719  * <pre><code>
45720 Ext.QuickTips.init(); // to display button quicktips
45721
45722 var myStore = new Ext.data.Store({
45723     reader: new Ext.data.JsonReader({
45724         {@link Ext.data.JsonReader#totalProperty totalProperty}: 'results', 
45725         ...
45726     }),
45727     ...
45728 });
45729
45730 var myPageSize = 25;  // server script should only send back 25 items at a time
45731
45732 var grid = new Ext.grid.GridPanel({
45733     ...
45734     store: myStore,
45735     bbar: new Ext.PagingToolbar({
45736         {@link #store}: myStore,       // grid and PagingToolbar using same store
45737         {@link #displayInfo}: true,
45738         {@link #pageSize}: myPageSize,
45739         {@link #prependButtons}: true,
45740         items: [
45741             'text 1'
45742         ]
45743     })
45744 });
45745  * </code></pre>
45746  *
45747  * <p>To use paging, pass the paging requirements to the server when the store is first loaded.</p>
45748  * <pre><code>
45749 store.load({
45750     params: {
45751         // specify params for the first page load if using paging
45752         start: 0,          
45753         limit: myPageSize,
45754         // other params
45755         foo:   'bar'
45756     }
45757 });
45758  * </code></pre>
45759  * 
45760  * <p>If using {@link Ext.data.Store#autoLoad store's autoLoad} configuration:</p>
45761  * <pre><code>
45762 var myStore = new Ext.data.Store({
45763     {@link Ext.data.Store#autoLoad autoLoad}: {params:{start: 0, limit: 25}},
45764     ...
45765 });
45766  * </code></pre>
45767  * 
45768  * <p>The packet sent back from the server would have this form:</p>
45769  * <pre><code>
45770 {
45771     "success": true,
45772     "results": 2000, 
45773     "rows": [ // <b>*Note:</b> this must be an Array 
45774         { "id":  1, "name": "Bill", "occupation": "Gardener" },
45775         { "id":  2, "name":  "Ben", "occupation": "Horticulturalist" },
45776         ...
45777         { "id": 25, "name":  "Sue", "occupation": "Botanist" }
45778     ]
45779 }
45780  * </code></pre>
45781  * <p><u>Paging with Local Data</u></p>
45782  * <p>Paging can also be accomplished with local data using extensions:</p>
45783  * <div class="mdetail-params"><ul>
45784  * <li><a href="http://extjs.com/forum/showthread.php?t=71532">Ext.ux.data.PagingStore</a></li>
45785  * <li>Paging Memory Proxy (examples/ux/PagingMemoryProxy.js)</li>
45786  * </ul></div>
45787  * @constructor Create a new PagingToolbar
45788  * @param {Object} config The config object
45789  * @xtype paging
45790  */
45791 (function() {
45792
45793 var T = Ext.Toolbar;
45794
45795 Ext.PagingToolbar = Ext.extend(Ext.Toolbar, {
45796     /**
45797      * @cfg {Ext.data.Store} store
45798      * The {@link Ext.data.Store} the paging toolbar should use as its data source (required).
45799      */
45800     /**
45801      * @cfg {Boolean} displayInfo
45802      * <tt>true</tt> to display the displayMsg (defaults to <tt>false</tt>)
45803      */
45804     /**
45805      * @cfg {Number} pageSize
45806      * The number of records to display per page (defaults to <tt>20</tt>)
45807      */
45808     pageSize : 20,
45809     /**
45810      * @cfg {Boolean} prependButtons
45811      * <tt>true</tt> to insert any configured <tt>items</tt> <i>before</i> the paging buttons.
45812      * Defaults to <tt>false</tt>.
45813      */
45814     /**
45815      * @cfg {String} displayMsg
45816      * The paging status message to display (defaults to <tt>'Displaying {0} - {1} of {2}'</tt>).
45817      * Note that this string is formatted using the braced numbers <tt>{0}-{2}</tt> as tokens
45818      * that are replaced by the values for start, end and total respectively. These tokens should
45819      * be preserved when overriding this string if showing those values is desired.
45820      */
45821     displayMsg : 'Displaying {0} - {1} of {2}',
45822     /**
45823      * @cfg {String} emptyMsg
45824      * The message to display when no records are found (defaults to 'No data to display')
45825      */
45826     emptyMsg : 'No data to display',
45827     /**
45828      * @cfg {String} beforePageText
45829      * The text displayed before the input item (defaults to <tt>'Page'</tt>).
45830      */
45831     beforePageText : 'Page',
45832     /**
45833      * @cfg {String} afterPageText
45834      * Customizable piece of the default paging text (defaults to <tt>'of {0}'</tt>). Note that
45835      * this string is formatted using <tt>{0}</tt> as a token that is replaced by the number of
45836      * total pages. This token should be preserved when overriding this string if showing the
45837      * total page count is desired.
45838      */
45839     afterPageText : 'of {0}',
45840     /**
45841      * @cfg {String} firstText
45842      * The quicktip text displayed for the first page button (defaults to <tt>'First Page'</tt>).
45843      * <b>Note</b>: quick tips must be initialized for the quicktip to show.
45844      */
45845     firstText : 'First Page',
45846     /**
45847      * @cfg {String} prevText
45848      * The quicktip text displayed for the previous page button (defaults to <tt>'Previous Page'</tt>).
45849      * <b>Note</b>: quick tips must be initialized for the quicktip to show.
45850      */
45851     prevText : 'Previous Page',
45852     /**
45853      * @cfg {String} nextText
45854      * The quicktip text displayed for the next page button (defaults to <tt>'Next Page'</tt>).
45855      * <b>Note</b>: quick tips must be initialized for the quicktip to show.
45856      */
45857     nextText : 'Next Page',
45858     /**
45859      * @cfg {String} lastText
45860      * The quicktip text displayed for the last page button (defaults to <tt>'Last Page'</tt>).
45861      * <b>Note</b>: quick tips must be initialized for the quicktip to show.
45862      */
45863     lastText : 'Last Page',
45864     /**
45865      * @cfg {String} refreshText
45866      * The quicktip text displayed for the Refresh button (defaults to <tt>'Refresh'</tt>).
45867      * <b>Note</b>: quick tips must be initialized for the quicktip to show.
45868      */
45869     refreshText : 'Refresh',
45870
45871     /**
45872      * <p><b>Deprecated</b>. <code>paramNames</code> should be set in the <b>data store</b>
45873      * (see {@link Ext.data.Store#paramNames}).</p>
45874      * <br><p>Object mapping of parameter names used for load calls, initially set to:</p>
45875      * <pre>{start: 'start', limit: 'limit'}</pre>
45876      * @type Object
45877      * @property paramNames
45878      * @deprecated
45879      */
45880
45881     /**
45882      * The number of records to display per page.  See also <tt>{@link #cursor}</tt>.
45883      * @type Number
45884      * @property pageSize
45885      */
45886
45887     /**
45888      * Indicator for the record position.  This property might be used to get the active page
45889      * number for example:<pre><code>
45890      * // t is reference to the paging toolbar instance
45891      * var activePage = Math.ceil((t.cursor + t.pageSize) / t.pageSize);
45892      * </code></pre>
45893      * @type Number
45894      * @property cursor
45895      */
45896
45897     initComponent : function(){
45898         var pagingItems = [this.first = new T.Button({
45899             tooltip: this.firstText,
45900             overflowText: this.firstText,
45901             iconCls: 'x-tbar-page-first',
45902             disabled: true,
45903             handler: this.moveFirst,
45904             scope: this
45905         }), this.prev = new T.Button({
45906             tooltip: this.prevText,
45907             overflowText: this.prevText,
45908             iconCls: 'x-tbar-page-prev',
45909             disabled: true,
45910             handler: this.movePrevious,
45911             scope: this
45912         }), '-', this.beforePageText,
45913         this.inputItem = new Ext.form.NumberField({
45914             cls: 'x-tbar-page-number',
45915             allowDecimals: false,
45916             allowNegative: false,
45917             enableKeyEvents: true,
45918             selectOnFocus: true,
45919             submitValue: false,
45920             listeners: {
45921                 scope: this,
45922                 keydown: this.onPagingKeyDown,
45923                 blur: this.onPagingBlur
45924             }
45925         }), this.afterTextItem = new T.TextItem({
45926             text: String.format(this.afterPageText, 1)
45927         }), '-', this.next = new T.Button({
45928             tooltip: this.nextText,
45929             overflowText: this.nextText,
45930             iconCls: 'x-tbar-page-next',
45931             disabled: true,
45932             handler: this.moveNext,
45933             scope: this
45934         }), this.last = new T.Button({
45935             tooltip: this.lastText,
45936             overflowText: this.lastText,
45937             iconCls: 'x-tbar-page-last',
45938             disabled: true,
45939             handler: this.moveLast,
45940             scope: this
45941         }), '-', this.refresh = new T.Button({
45942             tooltip: this.refreshText,
45943             overflowText: this.refreshText,
45944             iconCls: 'x-tbar-loading',
45945             handler: this.doRefresh,
45946             scope: this
45947         })];
45948
45949
45950         var userItems = this.items || this.buttons || [];
45951         if (this.prependButtons) {
45952             this.items = userItems.concat(pagingItems);
45953         }else{
45954             this.items = pagingItems.concat(userItems);
45955         }
45956         delete this.buttons;
45957         if(this.displayInfo){
45958             this.items.push('->');
45959             this.items.push(this.displayItem = new T.TextItem({}));
45960         }
45961         Ext.PagingToolbar.superclass.initComponent.call(this);
45962         this.addEvents(
45963             /**
45964              * @event change
45965              * Fires after the active page has been changed.
45966              * @param {Ext.PagingToolbar} this
45967              * @param {Object} pageData An object that has these properties:<ul>
45968              * <li><code>total</code> : Number <div class="sub-desc">The total number of records in the dataset as
45969              * returned by the server</div></li>
45970              * <li><code>activePage</code> : Number <div class="sub-desc">The current page number</div></li>
45971              * <li><code>pages</code> : Number <div class="sub-desc">The total number of pages (calculated from
45972              * the total number of records in the dataset as returned by the server and the current {@link #pageSize})</div></li>
45973              * </ul>
45974              */
45975             'change',
45976             /**
45977              * @event beforechange
45978              * Fires just before the active page is changed.
45979              * Return false to prevent the active page from being changed.
45980              * @param {Ext.PagingToolbar} this
45981              * @param {Object} params An object hash of the parameters which the PagingToolbar will send when
45982              * loading the required page. This will contain:<ul>
45983              * <li><code>start</code> : Number <div class="sub-desc">The starting row number for the next page of records to
45984              * be retrieved from the server</div></li>
45985              * <li><code>limit</code> : Number <div class="sub-desc">The number of records to be retrieved from the server</div></li>
45986              * </ul>
45987              * <p>(note: the names of the <b>start</b> and <b>limit</b> properties are determined
45988              * by the store's {@link Ext.data.Store#paramNames paramNames} property.)</p>
45989              * <p>Parameters may be added as required in the event handler.</p>
45990              */
45991             'beforechange'
45992         );
45993         this.on('afterlayout', this.onFirstLayout, this, {single: true});
45994         this.cursor = 0;
45995         this.bindStore(this.store, true);
45996     },
45997
45998     // private
45999     onFirstLayout : function(){
46000         if(this.dsLoaded){
46001             this.onLoad.apply(this, this.dsLoaded);
46002         }
46003     },
46004
46005     // private
46006     updateInfo : function(){
46007         if(this.displayItem){
46008             var count = this.store.getCount();
46009             var msg = count == 0 ?
46010                 this.emptyMsg :
46011                 String.format(
46012                     this.displayMsg,
46013                     this.cursor+1, this.cursor+count, this.store.getTotalCount()
46014                 );
46015             this.displayItem.setText(msg);
46016         }
46017     },
46018
46019     // private
46020     onLoad : function(store, r, o){
46021         if(!this.rendered){
46022             this.dsLoaded = [store, r, o];
46023             return;
46024         }
46025         var p = this.getParams();
46026         this.cursor = (o.params && o.params[p.start]) ? o.params[p.start] : 0;
46027         var d = this.getPageData(), ap = d.activePage, ps = d.pages;
46028
46029         this.afterTextItem.setText(String.format(this.afterPageText, d.pages));
46030         this.inputItem.setValue(ap);
46031         this.first.setDisabled(ap == 1);
46032         this.prev.setDisabled(ap == 1);
46033         this.next.setDisabled(ap == ps);
46034         this.last.setDisabled(ap == ps);
46035         this.refresh.enable();
46036         this.updateInfo();
46037         this.fireEvent('change', this, d);
46038     },
46039
46040     // private
46041     getPageData : function(){
46042         var total = this.store.getTotalCount();
46043         return {
46044             total : total,
46045             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
46046             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
46047         };
46048     },
46049
46050     /**
46051      * Change the active page
46052      * @param {Integer} page The page to display
46053      */
46054     changePage : function(page){
46055         this.doLoad(((page-1) * this.pageSize).constrain(0, this.store.getTotalCount()));
46056     },
46057
46058     // private
46059     onLoadError : function(){
46060         if(!this.rendered){
46061             return;
46062         }
46063         this.refresh.enable();
46064     },
46065
46066     // private
46067     readPage : function(d){
46068         var v = this.inputItem.getValue(), pageNum;
46069         if (!v || isNaN(pageNum = parseInt(v, 10))) {
46070             this.inputItem.setValue(d.activePage);
46071             return false;
46072         }
46073         return pageNum;
46074     },
46075
46076     onPagingFocus : function(){
46077         this.inputItem.select();
46078     },
46079
46080     //private
46081     onPagingBlur : function(e){
46082         this.inputItem.setValue(this.getPageData().activePage);
46083     },
46084
46085     // private
46086     onPagingKeyDown : function(field, e){
46087         var k = e.getKey(), d = this.getPageData(), pageNum;
46088         if (k == e.RETURN) {
46089             e.stopEvent();
46090             pageNum = this.readPage(d);
46091             if(pageNum !== false){
46092                 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
46093                 this.doLoad(pageNum * this.pageSize);
46094             }
46095         }else if (k == e.HOME || k == e.END){
46096             e.stopEvent();
46097             pageNum = k == e.HOME ? 1 : d.pages;
46098             field.setValue(pageNum);
46099         }else if (k == e.UP || k == e.PAGEUP || k == e.DOWN || k == e.PAGEDOWN){
46100             e.stopEvent();
46101             if((pageNum = this.readPage(d))){
46102                 var increment = e.shiftKey ? 10 : 1;
46103                 if(k == e.DOWN || k == e.PAGEDOWN){
46104                     increment *= -1;
46105                 }
46106                 pageNum += increment;
46107                 if(pageNum >= 1 & pageNum <= d.pages){
46108                     field.setValue(pageNum);
46109                 }
46110             }
46111         }
46112     },
46113
46114     // private
46115     getParams : function(){
46116         //retain backwards compat, allow params on the toolbar itself, if they exist.
46117         return this.paramNames || this.store.paramNames;
46118     },
46119
46120     // private
46121     beforeLoad : function(){
46122         if(this.rendered && this.refresh){
46123             this.refresh.disable();
46124         }
46125     },
46126
46127     // private
46128     doLoad : function(start){
46129         var o = {}, pn = this.getParams();
46130         o[pn.start] = start;
46131         o[pn.limit] = this.pageSize;
46132         if(this.fireEvent('beforechange', this, o) !== false){
46133             this.store.load({params:o});
46134         }
46135     },
46136
46137     /**
46138      * Move to the first page, has the same effect as clicking the 'first' button.
46139      */
46140     moveFirst : function(){
46141         this.doLoad(0);
46142     },
46143
46144     /**
46145      * Move to the previous page, has the same effect as clicking the 'previous' button.
46146      */
46147     movePrevious : function(){
46148         this.doLoad(Math.max(0, this.cursor-this.pageSize));
46149     },
46150
46151     /**
46152      * Move to the next page, has the same effect as clicking the 'next' button.
46153      */
46154     moveNext : function(){
46155         this.doLoad(this.cursor+this.pageSize);
46156     },
46157
46158     /**
46159      * Move to the last page, has the same effect as clicking the 'last' button.
46160      */
46161     moveLast : function(){
46162         var total = this.store.getTotalCount(),
46163             extra = total % this.pageSize;
46164
46165         this.doLoad(extra ? (total - extra) : total - this.pageSize);
46166     },
46167
46168     /**
46169      * Refresh the current page, has the same effect as clicking the 'refresh' button.
46170      */
46171     doRefresh : function(){
46172         this.doLoad(this.cursor);
46173     },
46174
46175     /**
46176      * Binds the paging toolbar to the specified {@link Ext.data.Store}
46177      * @param {Store} store The store to bind to this toolbar
46178      * @param {Boolean} initial (Optional) true to not remove listeners
46179      */
46180     bindStore : function(store, initial){
46181         var doLoad;
46182         if(!initial && this.store){
46183             if(store !== this.store && this.store.autoDestroy){
46184                 this.store.destroy();
46185             }else{
46186                 this.store.un('beforeload', this.beforeLoad, this);
46187                 this.store.un('load', this.onLoad, this);
46188                 this.store.un('exception', this.onLoadError, this);
46189             }
46190             if(!store){
46191                 this.store = null;
46192             }
46193         }
46194         if(store){
46195             store = Ext.StoreMgr.lookup(store);
46196             store.on({
46197                 scope: this,
46198                 beforeload: this.beforeLoad,
46199                 load: this.onLoad,
46200                 exception: this.onLoadError
46201             });
46202             doLoad = true;
46203         }
46204         this.store = store;
46205         if(doLoad){
46206             this.onLoad(store, null, {});
46207         }
46208     },
46209
46210     /**
46211      * Unbinds the paging toolbar from the specified {@link Ext.data.Store} <b>(deprecated)</b>
46212      * @param {Ext.data.Store} store The data store to unbind
46213      */
46214     unbind : function(store){
46215         this.bindStore(null);
46216     },
46217
46218     /**
46219      * Binds the paging toolbar to the specified {@link Ext.data.Store} <b>(deprecated)</b>
46220      * @param {Ext.data.Store} store The data store to bind
46221      */
46222     bind : function(store){
46223         this.bindStore(store);
46224     },
46225
46226     // private
46227     onDestroy : function(){
46228         this.bindStore(null);
46229         Ext.PagingToolbar.superclass.onDestroy.call(this);
46230     }
46231 });
46232
46233 })();
46234 Ext.reg('paging', Ext.PagingToolbar);/**\r
46235  * @class Ext.History\r
46236  * @extends Ext.util.Observable\r
46237  * History management component that allows you to register arbitrary tokens that signify application\r
46238  * history state on navigation actions.  You can then handle the history {@link #change} event in order\r
46239  * to reset your application UI to the appropriate state when the user navigates forward or backward through\r
46240  * the browser history stack.\r
46241  * @singleton\r
46242  */\r
46243 Ext.History = (function () {\r
46244     var iframe, hiddenField;\r
46245     var ready = false;\r
46246     var currentToken;\r
46247 \r
46248     function getHash() {\r
46249         var href = top.location.href, i = href.indexOf("#");\r
46250         return i >= 0 ? href.substr(i + 1) : null;\r
46251     }\r
46252 \r
46253     function doSave() {\r
46254         hiddenField.value = currentToken;\r
46255     }\r
46256 \r
46257     function handleStateChange(token) {\r
46258         currentToken = token;\r
46259         Ext.History.fireEvent('change', token);\r
46260     }\r
46261 \r
46262     function updateIFrame (token) {\r
46263         var html = ['<html><body><div id="state">',Ext.util.Format.htmlEncode(token),'</div></body></html>'].join('');\r
46264         try {\r
46265             var doc = iframe.contentWindow.document;\r
46266             doc.open();\r
46267             doc.write(html);\r
46268             doc.close();\r
46269             return true;\r
46270         } catch (e) {\r
46271             return false;\r
46272         }\r
46273     }\r
46274 \r
46275     function checkIFrame() {\r
46276         if (!iframe.contentWindow || !iframe.contentWindow.document) {\r
46277             setTimeout(checkIFrame, 10);\r
46278             return;\r
46279         }\r
46280 \r
46281         var doc = iframe.contentWindow.document;\r
46282         var elem = doc.getElementById("state");\r
46283         var token = elem ? elem.innerText : null;\r
46284 \r
46285         var hash = getHash();\r
46286 \r
46287         setInterval(function () {\r
46288 \r
46289             doc = iframe.contentWindow.document;\r
46290             elem = doc.getElementById("state");\r
46291 \r
46292             var newtoken = elem ? elem.innerText : null;\r
46293 \r
46294             var newHash = getHash();\r
46295 \r
46296             if (newtoken !== token) {\r
46297                 token = newtoken;\r
46298                 handleStateChange(token);\r
46299                 top.location.hash = token;\r
46300                 hash = token;\r
46301                 doSave();\r
46302             } else if (newHash !== hash) {\r
46303                 hash = newHash;\r
46304                 updateIFrame(newHash);\r
46305             }\r
46306 \r
46307         }, 50);\r
46308 \r
46309         ready = true;\r
46310 \r
46311         Ext.History.fireEvent('ready', Ext.History);\r
46312     }\r
46313 \r
46314     function startUp() {\r
46315         currentToken = hiddenField.value ? hiddenField.value : getHash();\r
46316 \r
46317         if (Ext.isIE) {\r
46318             checkIFrame();\r
46319         } else {\r
46320             var hash = getHash();\r
46321             setInterval(function () {\r
46322                 var newHash = getHash();\r
46323                 if (newHash !== hash) {\r
46324                     hash = newHash;\r
46325                     handleStateChange(hash);\r
46326                     doSave();\r
46327                 }\r
46328             }, 50);\r
46329             ready = true;\r
46330             Ext.History.fireEvent('ready', Ext.History);\r
46331         }\r
46332     }\r
46333 \r
46334     return {\r
46335         /**\r
46336          * The id of the hidden field required for storing the current history token.\r
46337          * @type String\r
46338          * @property\r
46339          */\r
46340         fieldId: 'x-history-field',\r
46341         /**\r
46342          * The id of the iframe required by IE to manage the history stack.\r
46343          * @type String\r
46344          * @property\r
46345          */\r
46346         iframeId: 'x-history-frame',\r
46347 \r
46348         events:{},\r
46349 \r
46350         /**\r
46351          * Initialize the global History instance.\r
46352          * @param {Boolean} onReady (optional) A callback function that will be called once the history\r
46353          * component is fully initialized.\r
46354          * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the callback is executed. Defaults to the browser window.\r
46355          */\r
46356         init: function (onReady, scope) {\r
46357             if(ready) {\r
46358                 Ext.callback(onReady, scope, [this]);\r
46359                 return;\r
46360             }\r
46361             if(!Ext.isReady){\r
46362                 Ext.onReady(function(){\r
46363                     Ext.History.init(onReady, scope);\r
46364                 });\r
46365                 return;\r
46366             }\r
46367             hiddenField = Ext.getDom(Ext.History.fieldId);\r
46368             if (Ext.isIE) {\r
46369                 iframe = Ext.getDom(Ext.History.iframeId);\r
46370             }\r
46371             this.addEvents(\r
46372                 /**\r
46373                  * @event ready\r
46374                  * Fires when the Ext.History singleton has been initialized and is ready for use.\r
46375                  * @param {Ext.History} The Ext.History singleton.\r
46376                  */\r
46377                 'ready',\r
46378                 /**\r
46379                  * @event change\r
46380                  * Fires when navigation back or forwards within the local page's history occurs.\r
46381                  * @param {String} token An identifier associated with the page state at that point in its history.\r
46382                  */\r
46383                 'change'\r
46384             );\r
46385             if(onReady){\r
46386                 this.on('ready', onReady, scope, {single:true});\r
46387             }\r
46388             startUp();\r
46389         },\r
46390 \r
46391         /**\r
46392          * Add a new token to the history stack. This can be any arbitrary value, although it would\r
46393          * commonly be the concatenation of a component id and another id marking the specifc history\r
46394          * state of that component.  Example usage:\r
46395          * <pre><code>\r
46396 // Handle tab changes on a TabPanel\r
46397 tabPanel.on('tabchange', function(tabPanel, tab){\r
46398     Ext.History.add(tabPanel.id + ':' + tab.id);\r
46399 });\r
46400 </code></pre>\r
46401          * @param {String} token The value that defines a particular application-specific history state\r
46402          * @param {Boolean} preventDuplicates When true, if the passed token matches the current token\r
46403          * it will not save a new history step. Set to false if the same state can be saved more than once\r
46404          * at the same history stack location (defaults to true).\r
46405          */\r
46406         add: function (token, preventDup) {\r
46407             if(preventDup !== false){\r
46408                 if(this.getToken() == token){\r
46409                     return true;\r
46410                 }\r
46411             }\r
46412             if (Ext.isIE) {\r
46413                 return updateIFrame(token);\r
46414             } else {\r
46415                 top.location.hash = token;\r
46416                 return true;\r
46417             }\r
46418         },\r
46419 \r
46420         /**\r
46421          * Programmatically steps back one step in browser history (equivalent to the user pressing the Back button).\r
46422          */\r
46423         back: function(){\r
46424             history.go(-1);\r
46425         },\r
46426 \r
46427         /**\r
46428          * Programmatically steps forward one step in browser history (equivalent to the user pressing the Forward button).\r
46429          */\r
46430         forward: function(){\r
46431             history.go(1);\r
46432         },\r
46433 \r
46434         /**\r
46435          * Retrieves the currently-active history token.\r
46436          * @return {String} The token\r
46437          */\r
46438         getToken: function() {\r
46439             return ready ? currentToken : getHash();\r
46440         }\r
46441     };\r
46442 })();\r
46443 Ext.apply(Ext.History, new Ext.util.Observable());/**\r
46444  * @class Ext.Tip\r
46445  * @extends Ext.Panel\r
46446  * @xtype tip\r
46447  * This is the base class for {@link Ext.QuickTip} and {@link Ext.Tooltip} that provides the basic layout and\r
46448  * positioning that all tip-based classes require. This class can be used directly for simple, statically-positioned\r
46449  * tips that are displayed programmatically, or it can be extended to provide custom tip implementations.\r
46450  * @constructor\r
46451  * Create a new Tip\r
46452  * @param {Object} config The configuration options\r
46453  */\r
46454 Ext.Tip = Ext.extend(Ext.Panel, {\r
46455     /**\r
46456      * @cfg {Boolean} closable True to render a close tool button into the tooltip header (defaults to false).\r
46457      */\r
46458     /**\r
46459      * @cfg {Number} width\r
46460      * Width in pixels of the tip (defaults to auto).  Width will be ignored if it exceeds the bounds of\r
46461      * {@link #minWidth} or {@link #maxWidth}.  The maximum supported value is 500.\r
46462      */\r
46463     /**\r
46464      * @cfg {Number} minWidth The minimum width of the tip in pixels (defaults to 40).\r
46465      */\r
46466     minWidth : 40,\r
46467     /**\r
46468      * @cfg {Number} maxWidth The maximum width of the tip in pixels (defaults to 300).  The maximum supported value is 500.\r
46469      */\r
46470     maxWidth : 300,\r
46471     /**\r
46472      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"\r
46473      * for bottom-right shadow (defaults to "sides").\r
46474      */\r
46475     shadow : "sides",\r
46476     /**\r
46477      * @cfg {String} defaultAlign <b>Experimental</b>. The default {@link Ext.Element#alignTo} anchor position value\r
46478      * for this tip relative to its element of origin (defaults to "tl-bl?").\r
46479      */\r
46480     defaultAlign : "tl-bl?",\r
46481     autoRender: true,\r
46482     quickShowInterval : 250,\r
46483 \r
46484     // private panel overrides\r
46485     frame:true,\r
46486     hidden:true,\r
46487     baseCls: 'x-tip',\r
46488     floating:{shadow:true,shim:true,useDisplay:true,constrain:false},\r
46489     autoHeight:true,\r
46490 \r
46491     closeAction: 'hide',\r
46492 \r
46493     // private\r
46494     initComponent : function(){\r
46495         Ext.Tip.superclass.initComponent.call(this);\r
46496         if(this.closable && !this.title){\r
46497             this.elements += ',header';\r
46498         }\r
46499     },\r
46500 \r
46501     // private\r
46502     afterRender : function(){\r
46503         Ext.Tip.superclass.afterRender.call(this);\r
46504         if(this.closable){\r
46505             this.addTool({\r
46506                 id: 'close',\r
46507                 handler: this[this.closeAction],\r
46508                 scope: this\r
46509             });\r
46510         }\r
46511     },\r
46512 \r
46513     /**\r
46514      * Shows this tip at the specified XY position.  Example usage:\r
46515      * <pre><code>\r
46516 // Show the tip at x:50 and y:100\r
46517 tip.showAt([50,100]);\r
46518 </code></pre>\r
46519      * @param {Array} xy An array containing the x and y coordinates\r
46520      */\r
46521     showAt : function(xy){\r
46522         Ext.Tip.superclass.show.call(this);\r
46523         if(this.measureWidth !== false && (!this.initialConfig || typeof this.initialConfig.width != 'number')){\r
46524             this.doAutoWidth();\r
46525         }\r
46526         if(this.constrainPosition){\r
46527             xy = this.el.adjustForConstraints(xy);\r
46528         }\r
46529         this.setPagePosition(xy[0], xy[1]);\r
46530     },\r
46531 \r
46532     // protected\r
46533     doAutoWidth : function(adjust){\r
46534         adjust = adjust || 0;\r
46535         var bw = this.body.getTextWidth();\r
46536         if(this.title){\r
46537             bw = Math.max(bw, this.header.child('span').getTextWidth(this.title));\r
46538         }\r
46539         bw += this.getFrameWidth() + (this.closable ? 20 : 0) + this.body.getPadding("lr") + adjust;\r
46540         this.setWidth(bw.constrain(this.minWidth, this.maxWidth));\r
46541         \r
46542         // IE7 repaint bug on initial show\r
46543         if(Ext.isIE7 && !this.repainted){\r
46544             this.el.repaint();\r
46545             this.repainted = true;\r
46546         }\r
46547     },\r
46548 \r
46549     /**\r
46550      * <b>Experimental</b>. Shows this tip at a position relative to another element using a standard {@link Ext.Element#alignTo}\r
46551      * anchor position value.  Example usage:\r
46552      * <pre><code>\r
46553 // Show the tip at the default position ('tl-br?')\r
46554 tip.showBy('my-el');\r
46555 \r
46556 // Show the tip's top-left corner anchored to the element's top-right corner\r
46557 tip.showBy('my-el', 'tl-tr');\r
46558 </code></pre>\r
46559      * @param {Mixed} el An HTMLElement, Ext.Element or string id of the target element to align to\r
46560      * @param {String} position (optional) A valid {@link Ext.Element#alignTo} anchor position (defaults to 'tl-br?' or\r
46561      * {@link #defaultAlign} if specified).\r
46562      */\r
46563     showBy : function(el, pos){\r
46564         if(!this.rendered){\r
46565             this.render(Ext.getBody());\r
46566         }\r
46567         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign));\r
46568     },\r
46569 \r
46570     initDraggable : function(){\r
46571         this.dd = new Ext.Tip.DD(this, typeof this.draggable == 'boolean' ? null : this.draggable);\r
46572         this.header.addClass('x-tip-draggable');\r
46573     }\r
46574 });\r
46575 \r
46576 Ext.reg('tip', Ext.Tip);\r
46577 \r
46578 // private - custom Tip DD implementation\r
46579 Ext.Tip.DD = function(tip, config){\r
46580     Ext.apply(this, config);\r
46581     this.tip = tip;\r
46582     Ext.Tip.DD.superclass.constructor.call(this, tip.el.id, 'WindowDD-'+tip.id);\r
46583     this.setHandleElId(tip.header.id);\r
46584     this.scroll = false;\r
46585 };\r
46586 \r
46587 Ext.extend(Ext.Tip.DD, Ext.dd.DD, {\r
46588     moveOnly:true,\r
46589     scroll:false,\r
46590     headerOffsets:[100, 25],\r
46591     startDrag : function(){\r
46592         this.tip.el.disableShadow();\r
46593     },\r
46594     endDrag : function(e){\r
46595         this.tip.el.enableShadow(true);\r
46596     }\r
46597 });/**\r
46598  * @class Ext.ToolTip\r
46599  * @extends Ext.Tip\r
46600  * A standard tooltip implementation for providing additional information when hovering over a target element.\r
46601  * @xtype tooltip\r
46602  * @constructor\r
46603  * Create a new Tooltip\r
46604  * @param {Object} config The configuration options\r
46605  */\r
46606 Ext.ToolTip = Ext.extend(Ext.Tip, {\r
46607     /**\r
46608      * When a Tooltip is configured with the <code>{@link #delegate}</code>\r
46609      * option to cause selected child elements of the <code>{@link #target}</code>\r
46610      * Element to each trigger a seperate show event, this property is set to\r
46611      * the DOM element which triggered the show.\r
46612      * @type DOMElement\r
46613      * @property triggerElement\r
46614      */\r
46615     /**\r
46616      * @cfg {Mixed} target The target HTMLElement, Ext.Element or id to monitor\r
46617      * for mouseover events to trigger showing this ToolTip.\r
46618      */\r
46619     /**\r
46620      * @cfg {Boolean} autoHide True to automatically hide the tooltip after the\r
46621      * mouse exits the target element or after the <code>{@link #dismissDelay}</code>\r
46622      * has expired if set (defaults to true).  If <code>{@link closable} = true</code>\r
46623      * a close tool button will be rendered into the tooltip header.\r
46624      */\r
46625     /**\r
46626      * @cfg {Number} showDelay Delay in milliseconds before the tooltip displays\r
46627      * after the mouse enters the target element (defaults to 500)\r
46628      */\r
46629     showDelay : 500,\r
46630     /**\r
46631      * @cfg {Number} hideDelay Delay in milliseconds after the mouse exits the\r
46632      * target element but before the tooltip actually hides (defaults to 200).\r
46633      * Set to 0 for the tooltip to hide immediately.\r
46634      */\r
46635     hideDelay : 200,\r
46636     /**\r
46637      * @cfg {Number} dismissDelay Delay in milliseconds before the tooltip\r
46638      * automatically hides (defaults to 5000). To disable automatic hiding, set\r
46639      * dismissDelay = 0.\r
46640      */\r
46641     dismissDelay : 5000,\r
46642     /**\r
46643      * @cfg {Array} mouseOffset An XY offset from the mouse position where the\r
46644      * tooltip should be shown (defaults to [15,18]).\r
46645      */\r
46646     /**\r
46647      * @cfg {Boolean} trackMouse True to have the tooltip follow the mouse as it\r
46648      * moves over the target element (defaults to false).\r
46649      */\r
46650     trackMouse : false,\r
46651     /**\r
46652      * @cfg {Boolean} anchorToTarget True to anchor the tooltip to the target\r
46653      * element, false to anchor it relative to the mouse coordinates (defaults\r
46654      * to true).  When <code>anchorToTarget</code> is true, use\r
46655      * <code>{@link #defaultAlign}</code> to control tooltip alignment to the\r
46656      * target element.  When <code>anchorToTarget</code> is false, use\r
46657      * <code>{@link #anchorPosition}</code> instead to control alignment.\r
46658      */\r
46659     anchorToTarget : true,\r
46660     /**\r
46661      * @cfg {Number} anchorOffset A numeric pixel value used to offset the\r
46662      * default position of the anchor arrow (defaults to 0).  When the anchor\r
46663      * position is on the top or bottom of the tooltip, <code>anchorOffset</code>\r
46664      * will be used as a horizontal offset.  Likewise, when the anchor position\r
46665      * is on the left or right side, <code>anchorOffset</code> will be used as\r
46666      * a vertical offset.\r
46667      */\r
46668     anchorOffset : 0,\r
46669     /**\r
46670      * @cfg {String} delegate <p>Optional. A {@link Ext.DomQuery DomQuery}\r
46671      * selector which allows selection of individual elements within the\r
46672      * <code>{@link #target}</code> element to trigger showing and hiding the\r
46673      * ToolTip as the mouse moves within the target.</p>\r
46674      * <p>When specified, the child element of the target which caused a show\r
46675      * event is placed into the <code>{@link #triggerElement}</code> property\r
46676      * before the ToolTip is shown.</p>\r
46677      * <p>This may be useful when a Component has regular, repeating elements\r
46678      * in it, each of which need a Tooltip which contains information specific\r
46679      * to that element. For example:</p><pre><code>\r
46680 var myGrid = new Ext.grid.gridPanel(gridConfig);\r
46681 myGrid.on('render', function(grid) {\r
46682     var store = grid.getStore();  // Capture the Store.\r
46683     var view = grid.getView();    // Capture the GridView.\r
46684     myGrid.tip = new Ext.ToolTip({\r
46685         target: view.mainBody,    // The overall target element.\r
46686         delegate: '.x-grid3-row', // Each grid row causes its own seperate show and hide.\r
46687         trackMouse: true,         // Moving within the row should not hide the tip.\r
46688         renderTo: document.body,  // Render immediately so that tip.body can be\r
46689                                   //  referenced prior to the first show.\r
46690         listeners: {              // Change content dynamically depending on which element\r
46691                                   //  triggered the show.\r
46692             beforeshow: function updateTipBody(tip) {\r
46693                 var rowIndex = view.findRowIndex(tip.triggerElement);\r
46694                 tip.body.dom.innerHTML = 'Over Record ID ' + store.getAt(rowIndex).id;\r
46695             }\r
46696         }\r
46697     });\r
46698 });\r
46699      *</code></pre>\r
46700      */\r
46701 \r
46702     // private\r
46703     targetCounter : 0,\r
46704 \r
46705     constrainPosition : false,\r
46706 \r
46707     // private\r
46708     initComponent : function(){\r
46709         Ext.ToolTip.superclass.initComponent.call(this);\r
46710         this.lastActive = new Date();\r
46711         this.initTarget(this.target);\r
46712         this.origAnchor = this.anchor;\r
46713     },\r
46714 \r
46715     // private\r
46716     onRender : function(ct, position){\r
46717         Ext.ToolTip.superclass.onRender.call(this, ct, position);\r
46718         this.anchorCls = 'x-tip-anchor-' + this.getAnchorPosition();\r
46719         this.anchorEl = this.el.createChild({\r
46720             cls: 'x-tip-anchor ' + this.anchorCls\r
46721         });\r
46722     },\r
46723 \r
46724     // private\r
46725     afterRender : function(){\r
46726         Ext.ToolTip.superclass.afterRender.call(this);\r
46727         this.anchorEl.setStyle('z-index', this.el.getZIndex() + 1);\r
46728     },\r
46729 \r
46730     /**\r
46731      * Binds this ToolTip to the specified element. The tooltip will be displayed when the mouse moves over the element.\r
46732      * @param {Mixed} t The Element, HtmlElement, or ID of an element to bind to\r
46733      */\r
46734     initTarget : function(target){\r
46735         var t;\r
46736         if((t = Ext.get(target))){\r
46737             if(this.target){\r
46738                 var tg = Ext.get(this.target);\r
46739                 this.mun(tg, 'mouseover', this.onTargetOver, this);\r
46740                 this.mun(tg, 'mouseout', this.onTargetOut, this);\r
46741                 this.mun(tg, 'mousemove', this.onMouseMove, this);\r
46742             }\r
46743             this.mon(t, {\r
46744                 mouseover: this.onTargetOver,\r
46745                 mouseout: this.onTargetOut,\r
46746                 mousemove: this.onMouseMove,\r
46747                 scope: this\r
46748             });\r
46749             this.target = t;\r
46750         }\r
46751         if(this.anchor){\r
46752             this.anchorTarget = this.target;\r
46753         }\r
46754     },\r
46755 \r
46756     // private\r
46757     onMouseMove : function(e){\r
46758         var t = this.delegate ? e.getTarget(this.delegate) : this.triggerElement = true;\r
46759         if (t) {\r
46760             this.targetXY = e.getXY();\r
46761             if (t === this.triggerElement) {\r
46762                 if(!this.hidden && this.trackMouse){\r
46763                     this.setPagePosition(this.getTargetXY());\r
46764                 }\r
46765             } else {\r
46766                 this.hide();\r
46767                 this.lastActive = new Date(0);\r
46768                 this.onTargetOver(e);\r
46769             }\r
46770         } else if (!this.closable && this.isVisible()) {\r
46771             this.hide();\r
46772         }\r
46773     },\r
46774 \r
46775     // private\r
46776     getTargetXY : function(){\r
46777         if(this.delegate){\r
46778             this.anchorTarget = this.triggerElement;\r
46779         }\r
46780         if(this.anchor){\r
46781             this.targetCounter++;\r
46782             var offsets = this.getOffsets(),\r
46783                 xy = (this.anchorToTarget && !this.trackMouse) ? this.el.getAlignToXY(this.anchorTarget, this.getAnchorAlign()) : this.targetXY,\r
46784                 dw = Ext.lib.Dom.getViewWidth() - 5,\r
46785                 dh = Ext.lib.Dom.getViewHeight() - 5,\r
46786                 de = document.documentElement,\r
46787                 bd = document.body,\r
46788                 scrollX = (de.scrollLeft || bd.scrollLeft || 0) + 5,\r
46789                 scrollY = (de.scrollTop || bd.scrollTop || 0) + 5,\r
46790                 axy = [xy[0] + offsets[0], xy[1] + offsets[1]],\r
46791                 sz = this.getSize();\r
46792                 \r
46793             this.anchorEl.removeClass(this.anchorCls);\r
46794 \r
46795             if(this.targetCounter < 2){\r
46796                 if(axy[0] < scrollX){\r
46797                     if(this.anchorToTarget){\r
46798                         this.defaultAlign = 'l-r';\r
46799                         if(this.mouseOffset){this.mouseOffset[0] *= -1;}\r
46800                     }\r
46801                     this.anchor = 'left';\r
46802                     return this.getTargetXY();\r
46803                 }\r
46804                 if(axy[0]+sz.width > dw){\r
46805                     if(this.anchorToTarget){\r
46806                         this.defaultAlign = 'r-l';\r
46807                         if(this.mouseOffset){this.mouseOffset[0] *= -1;}\r
46808                     }\r
46809                     this.anchor = 'right';\r
46810                     return this.getTargetXY();\r
46811                 }\r
46812                 if(axy[1] < scrollY){\r
46813                     if(this.anchorToTarget){\r
46814                         this.defaultAlign = 't-b';\r
46815                         if(this.mouseOffset){this.mouseOffset[1] *= -1;}\r
46816                     }\r
46817                     this.anchor = 'top';\r
46818                     return this.getTargetXY();\r
46819                 }\r
46820                 if(axy[1]+sz.height > dh){\r
46821                     if(this.anchorToTarget){\r
46822                         this.defaultAlign = 'b-t';\r
46823                         if(this.mouseOffset){this.mouseOffset[1] *= -1;}\r
46824                     }\r
46825                     this.anchor = 'bottom';\r
46826                     return this.getTargetXY();\r
46827                 }\r
46828             }\r
46829 \r
46830             this.anchorCls = 'x-tip-anchor-'+this.getAnchorPosition();\r
46831             this.anchorEl.addClass(this.anchorCls);\r
46832             this.targetCounter = 0;\r
46833             return axy;\r
46834         }else{\r
46835             var mouseOffset = this.getMouseOffset();\r
46836             return [this.targetXY[0]+mouseOffset[0], this.targetXY[1]+mouseOffset[1]];\r
46837         }\r
46838     },\r
46839 \r
46840     getMouseOffset : function(){\r
46841         var offset = this.anchor ? [0,0] : [15,18];\r
46842         if(this.mouseOffset){\r
46843             offset[0] += this.mouseOffset[0];\r
46844             offset[1] += this.mouseOffset[1];\r
46845         }\r
46846         return offset;\r
46847     },\r
46848 \r
46849     // private\r
46850     getAnchorPosition : function(){\r
46851         if(this.anchor){\r
46852             this.tipAnchor = this.anchor.charAt(0);\r
46853         }else{\r
46854             var m = this.defaultAlign.match(/^([a-z]+)-([a-z]+)(\?)?$/);\r
46855             if(!m){\r
46856                throw 'AnchorTip.defaultAlign is invalid';\r
46857             }\r
46858             this.tipAnchor = m[1].charAt(0);\r
46859         }\r
46860 \r
46861         switch(this.tipAnchor){\r
46862             case 't': return 'top';\r
46863             case 'b': return 'bottom';\r
46864             case 'r': return 'right';\r
46865         }\r
46866         return 'left';\r
46867     },\r
46868 \r
46869     // private\r
46870     getAnchorAlign : function(){\r
46871         switch(this.anchor){\r
46872             case 'top'  : return 'tl-bl';\r
46873             case 'left' : return 'tl-tr';\r
46874             case 'right': return 'tr-tl';\r
46875             default     : return 'bl-tl';\r
46876         }\r
46877     },\r
46878 \r
46879     // private\r
46880     getOffsets : function(){\r
46881         var offsets, \r
46882             ap = this.getAnchorPosition().charAt(0);\r
46883         if(this.anchorToTarget && !this.trackMouse){\r
46884             switch(ap){\r
46885                 case 't':\r
46886                     offsets = [0, 9];\r
46887                     break;\r
46888                 case 'b':\r
46889                     offsets = [0, -13];\r
46890                     break;\r
46891                 case 'r':\r
46892                     offsets = [-13, 0];\r
46893                     break;\r
46894                 default:\r
46895                     offsets = [9, 0];\r
46896                     break;\r
46897             }\r
46898         }else{\r
46899             switch(ap){\r
46900                 case 't':\r
46901                     offsets = [-15-this.anchorOffset, 30];\r
46902                     break;\r
46903                 case 'b':\r
46904                     offsets = [-19-this.anchorOffset, -13-this.el.dom.offsetHeight];\r
46905                     break;\r
46906                 case 'r':\r
46907                     offsets = [-15-this.el.dom.offsetWidth, -13-this.anchorOffset];\r
46908                     break;\r
46909                 default:\r
46910                     offsets = [25, -13-this.anchorOffset];\r
46911                     break;\r
46912             }\r
46913         }\r
46914         var mouseOffset = this.getMouseOffset();\r
46915         offsets[0] += mouseOffset[0];\r
46916         offsets[1] += mouseOffset[1];\r
46917 \r
46918         return offsets;\r
46919     },\r
46920 \r
46921     // private\r
46922     onTargetOver : function(e){\r
46923         if(this.disabled || e.within(this.target.dom, true)){\r
46924             return;\r
46925         }\r
46926         var t = e.getTarget(this.delegate);\r
46927         if (t) {\r
46928             this.triggerElement = t;\r
46929             this.clearTimer('hide');\r
46930             this.targetXY = e.getXY();\r
46931             this.delayShow();\r
46932         }\r
46933     },\r
46934 \r
46935     // private\r
46936     delayShow : function(){\r
46937         if(this.hidden && !this.showTimer){\r
46938             if(this.lastActive.getElapsed() < this.quickShowInterval){\r
46939                 this.show();\r
46940             }else{\r
46941                 this.showTimer = this.show.defer(this.showDelay, this);\r
46942             }\r
46943         }else if(!this.hidden && this.autoHide !== false){\r
46944             this.show();\r
46945         }\r
46946     },\r
46947 \r
46948     // private\r
46949     onTargetOut : function(e){\r
46950         if(this.disabled || e.within(this.target.dom, true)){\r
46951             return;\r
46952         }\r
46953         this.clearTimer('show');\r
46954         if(this.autoHide !== false){\r
46955             this.delayHide();\r
46956         }\r
46957     },\r
46958 \r
46959     // private\r
46960     delayHide : function(){\r
46961         if(!this.hidden && !this.hideTimer){\r
46962             this.hideTimer = this.hide.defer(this.hideDelay, this);\r
46963         }\r
46964     },\r
46965 \r
46966     /**\r
46967      * Hides this tooltip if visible.\r
46968      */\r
46969     hide: function(){\r
46970         this.clearTimer('dismiss');\r
46971         this.lastActive = new Date();\r
46972         if(this.anchorEl){\r
46973             this.anchorEl.hide();\r
46974         }\r
46975         Ext.ToolTip.superclass.hide.call(this);\r
46976         delete this.triggerElement;\r
46977     },\r
46978 \r
46979     /**\r
46980      * Shows this tooltip at the current event target XY position.\r
46981      */\r
46982     show : function(){\r
46983         if(this.anchor){\r
46984             // pre-show it off screen so that the el will have dimensions\r
46985             // for positioning calcs when getting xy next\r
46986             this.showAt([-1000,-1000]);\r
46987             this.origConstrainPosition = this.constrainPosition;\r
46988             this.constrainPosition = false;\r
46989             this.anchor = this.origAnchor;\r
46990         }\r
46991         this.showAt(this.getTargetXY());\r
46992 \r
46993         if(this.anchor){\r
46994             this.syncAnchor();\r
46995             this.anchorEl.show();\r
46996             this.constrainPosition = this.origConstrainPosition;\r
46997         }else{\r
46998             this.anchorEl.hide();\r
46999         }\r
47000     },\r
47001 \r
47002     // inherit docs\r
47003     showAt : function(xy){\r
47004         this.lastActive = new Date();\r
47005         this.clearTimers();\r
47006         Ext.ToolTip.superclass.showAt.call(this, xy);\r
47007         if(this.dismissDelay && this.autoHide !== false){\r
47008             this.dismissTimer = this.hide.defer(this.dismissDelay, this);\r
47009         }\r
47010         if(this.anchor && !this.anchorEl.isVisible()){\r
47011             this.syncAnchor();\r
47012             this.anchorEl.show();\r
47013         }\r
47014     },\r
47015 \r
47016     // private\r
47017     syncAnchor : function(){\r
47018         var anchorPos, targetPos, offset;\r
47019         switch(this.tipAnchor.charAt(0)){\r
47020             case 't':\r
47021                 anchorPos = 'b';\r
47022                 targetPos = 'tl';\r
47023                 offset = [20+this.anchorOffset, 2];\r
47024                 break;\r
47025             case 'r':\r
47026                 anchorPos = 'l';\r
47027                 targetPos = 'tr';\r
47028                 offset = [-2, 11+this.anchorOffset];\r
47029                 break;\r
47030             case 'b':\r
47031                 anchorPos = 't';\r
47032                 targetPos = 'bl';\r
47033                 offset = [20+this.anchorOffset, -2];\r
47034                 break;\r
47035             default:\r
47036                 anchorPos = 'r';\r
47037                 targetPos = 'tl';\r
47038                 offset = [2, 11+this.anchorOffset];\r
47039                 break;\r
47040         }\r
47041         this.anchorEl.alignTo(this.el, anchorPos+'-'+targetPos, offset);\r
47042     },\r
47043 \r
47044     // private\r
47045     setPagePosition : function(x, y){\r
47046         Ext.ToolTip.superclass.setPagePosition.call(this, x, y);\r
47047         if(this.anchor){\r
47048             this.syncAnchor();\r
47049         }\r
47050     },\r
47051 \r
47052     // private\r
47053     clearTimer : function(name){\r
47054         name = name + 'Timer';\r
47055         clearTimeout(this[name]);\r
47056         delete this[name];\r
47057     },\r
47058 \r
47059     // private\r
47060     clearTimers : function(){\r
47061         this.clearTimer('show');\r
47062         this.clearTimer('dismiss');\r
47063         this.clearTimer('hide');\r
47064     },\r
47065 \r
47066     // private\r
47067     onShow : function(){\r
47068         Ext.ToolTip.superclass.onShow.call(this);\r
47069         Ext.getDoc().on('mousedown', this.onDocMouseDown, this);\r
47070     },\r
47071 \r
47072     // private\r
47073     onHide : function(){\r
47074         Ext.ToolTip.superclass.onHide.call(this);\r
47075         Ext.getDoc().un('mousedown', this.onDocMouseDown, this);\r
47076     },\r
47077 \r
47078     // private\r
47079     onDocMouseDown : function(e){\r
47080         if(this.autoHide !== true && !this.closable && !e.within(this.el.dom)){\r
47081             this.disable();\r
47082             this.enable.defer(100, this);\r
47083         }\r
47084     },\r
47085 \r
47086     // private\r
47087     onDisable : function(){\r
47088         this.clearTimers();\r
47089         this.hide();\r
47090     },\r
47091 \r
47092     // private\r
47093     adjustPosition : function(x, y){\r
47094         if(this.contstrainPosition){\r
47095             var ay = this.targetXY[1], h = this.getSize().height;\r
47096             if(y <= ay && (y+h) >= ay){\r
47097                 y = ay-h-5;\r
47098             }\r
47099         }\r
47100         return {x : x, y: y};\r
47101     },\r
47102     \r
47103     beforeDestroy : function(){\r
47104         this.clearTimers();\r
47105         Ext.destroy(this.anchorEl);\r
47106         delete this.anchorEl;\r
47107         delete this.target;\r
47108         delete this.anchorTarget;\r
47109         delete this.triggerElement;\r
47110         Ext.ToolTip.superclass.beforeDestroy.call(this);    \r
47111     },\r
47112 \r
47113     // private\r
47114     onDestroy : function(){\r
47115         Ext.getDoc().un('mousedown', this.onDocMouseDown, this);\r
47116         Ext.ToolTip.superclass.onDestroy.call(this);\r
47117     }\r
47118 });\r
47119 \r
47120 Ext.reg('tooltip', Ext.ToolTip);/**\r
47121  * @class Ext.QuickTip\r
47122  * @extends Ext.ToolTip\r
47123  * @xtype quicktip\r
47124  * A specialized tooltip class for tooltips that can be specified in markup and automatically managed by the global\r
47125  * {@link Ext.QuickTips} instance.  See the QuickTips class header for additional usage details and examples.\r
47126  * @constructor\r
47127  * Create a new Tip\r
47128  * @param {Object} config The configuration options\r
47129  */\r
47130 Ext.QuickTip = Ext.extend(Ext.ToolTip, {\r
47131     /**\r
47132      * @cfg {Mixed} target The target HTMLElement, Ext.Element or id to associate with this quicktip (defaults to the document).\r
47133      */\r
47134     /**\r
47135      * @cfg {Boolean} interceptTitles True to automatically use the element's DOM title value if available (defaults to false).\r
47136      */\r
47137     interceptTitles : false,\r
47138 \r
47139     // private\r
47140     tagConfig : {\r
47141         namespace : "ext",\r
47142         attribute : "qtip",\r
47143         width : "qwidth",\r
47144         target : "target",\r
47145         title : "qtitle",\r
47146         hide : "hide",\r
47147         cls : "qclass",\r
47148         align : "qalign",\r
47149         anchor : "anchor"\r
47150     },\r
47151 \r
47152     // private\r
47153     initComponent : function(){\r
47154         this.target = this.target || Ext.getDoc();\r
47155         this.targets = this.targets || {};\r
47156         Ext.QuickTip.superclass.initComponent.call(this);\r
47157     },\r
47158 \r
47159     /**\r
47160      * Configures a new quick tip instance and assigns it to a target element.  The following config values are\r
47161      * supported (for example usage, see the {@link Ext.QuickTips} class header):\r
47162      * <div class="mdetail-params"><ul>\r
47163      * <li>autoHide</li>\r
47164      * <li>cls</li>\r
47165      * <li>dismissDelay (overrides the singleton value)</li>\r
47166      * <li>target (required)</li>\r
47167      * <li>text (required)</li>\r
47168      * <li>title</li>\r
47169      * <li>width</li></ul></div>\r
47170      * @param {Object} config The config object\r
47171      */\r
47172     register : function(config){\r
47173         var cs = Ext.isArray(config) ? config : arguments;\r
47174         for(var i = 0, len = cs.length; i < len; i++){\r
47175             var c = cs[i];\r
47176             var target = c.target;\r
47177             if(target){\r
47178                 if(Ext.isArray(target)){\r
47179                     for(var j = 0, jlen = target.length; j < jlen; j++){\r
47180                         this.targets[Ext.id(target[j])] = c;\r
47181                     }\r
47182                 } else{\r
47183                     this.targets[Ext.id(target)] = c;\r
47184                 }\r
47185             }\r
47186         }\r
47187     },\r
47188 \r
47189     /**\r
47190      * Removes this quick tip from its element and destroys it.\r
47191      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.\r
47192      */\r
47193     unregister : function(el){\r
47194         delete this.targets[Ext.id(el)];\r
47195     },\r
47196     \r
47197     /**\r
47198      * Hides a visible tip or cancels an impending show for a particular element.\r
47199      * @param {String/HTMLElement/Element} el The element that is the target of the tip.\r
47200      */\r
47201     cancelShow: function(el){\r
47202         var at = this.activeTarget;\r
47203         el = Ext.get(el).dom;\r
47204         if(this.isVisible()){\r
47205             if(at && at.el == el){\r
47206                 this.hide();\r
47207             }\r
47208         }else if(at && at.el == el){\r
47209             this.clearTimer('show');\r
47210         }\r
47211     },\r
47212     \r
47213     getTipCfg: function(e) {\r
47214         var t = e.getTarget(), \r
47215             ttp, \r
47216             cfg;\r
47217         if(this.interceptTitles && t.title && Ext.isString(t.title)){\r
47218             ttp = t.title;\r
47219             t.qtip = ttp;\r
47220             t.removeAttribute("title");\r
47221             e.preventDefault();\r
47222         }else{\r
47223             cfg = this.tagConfig;\r
47224             ttp = t.qtip || Ext.fly(t).getAttribute(cfg.attribute, cfg.namespace);\r
47225         }\r
47226         return ttp;\r
47227     },\r
47228 \r
47229     // private\r
47230     onTargetOver : function(e){\r
47231         if(this.disabled){\r
47232             return;\r
47233         }\r
47234         this.targetXY = e.getXY();\r
47235         var t = e.getTarget();\r
47236         if(!t || t.nodeType !== 1 || t == document || t == document.body){\r
47237             return;\r
47238         }\r
47239         if(this.activeTarget && ((t == this.activeTarget.el) || Ext.fly(this.activeTarget.el).contains(t))){\r
47240             this.clearTimer('hide');\r
47241             this.show();\r
47242             return;\r
47243         }\r
47244         if(t && this.targets[t.id]){\r
47245             this.activeTarget = this.targets[t.id];\r
47246             this.activeTarget.el = t;\r
47247             this.anchor = this.activeTarget.anchor;\r
47248             if(this.anchor){\r
47249                 this.anchorTarget = t;\r
47250             }\r
47251             this.delayShow();\r
47252             return;\r
47253         }\r
47254         var ttp, et = Ext.fly(t), cfg = this.tagConfig, ns = cfg.namespace;\r
47255         if(ttp = this.getTipCfg(e)){\r
47256             var autoHide = et.getAttribute(cfg.hide, ns);\r
47257             this.activeTarget = {\r
47258                 el: t,\r
47259                 text: ttp,\r
47260                 width: et.getAttribute(cfg.width, ns),\r
47261                 autoHide: autoHide != "user" && autoHide !== 'false',\r
47262                 title: et.getAttribute(cfg.title, ns),\r
47263                 cls: et.getAttribute(cfg.cls, ns),\r
47264                 align: et.getAttribute(cfg.align, ns)\r
47265                 \r
47266             };\r
47267             this.anchor = et.getAttribute(cfg.anchor, ns);\r
47268             if(this.anchor){\r
47269                 this.anchorTarget = t;\r
47270             }\r
47271             this.delayShow();\r
47272         }\r
47273     },\r
47274 \r
47275     // private\r
47276     onTargetOut : function(e){\r
47277 \r
47278         // If moving within the current target, and it does not have a new tip, ignore the mouseout\r
47279         if (this.activeTarget && e.within(this.activeTarget.el) && !this.getTipCfg(e)) {\r
47280             return;\r
47281         }\r
47282 \r
47283         this.clearTimer('show');\r
47284         if(this.autoHide !== false){\r
47285             this.delayHide();\r
47286         }\r
47287     },\r
47288 \r
47289     // inherit docs\r
47290     showAt : function(xy){\r
47291         var t = this.activeTarget;\r
47292         if(t){\r
47293             if(!this.rendered){\r
47294                 this.render(Ext.getBody());\r
47295                 this.activeTarget = t;\r
47296             }\r
47297             if(t.width){\r
47298                 this.setWidth(t.width);\r
47299                 this.body.setWidth(this.adjustBodyWidth(t.width - this.getFrameWidth()));\r
47300                 this.measureWidth = false;\r
47301             } else{\r
47302                 this.measureWidth = true;\r
47303             }\r
47304             this.setTitle(t.title || '');\r
47305             this.body.update(t.text);\r
47306             this.autoHide = t.autoHide;\r
47307             this.dismissDelay = t.dismissDelay || this.dismissDelay;\r
47308             if(this.lastCls){\r
47309                 this.el.removeClass(this.lastCls);\r
47310                 delete this.lastCls;\r
47311             }\r
47312             if(t.cls){\r
47313                 this.el.addClass(t.cls);\r
47314                 this.lastCls = t.cls;\r
47315             }\r
47316             if(this.anchor){\r
47317                 this.constrainPosition = false;\r
47318             }else if(t.align){ // TODO: this doesn't seem to work consistently\r
47319                 xy = this.el.getAlignToXY(t.el, t.align);\r
47320                 this.constrainPosition = false;\r
47321             }else{\r
47322                 this.constrainPosition = true;\r
47323             }\r
47324         }\r
47325         Ext.QuickTip.superclass.showAt.call(this, xy);\r
47326     },\r
47327 \r
47328     // inherit docs\r
47329     hide: function(){\r
47330         delete this.activeTarget;\r
47331         Ext.QuickTip.superclass.hide.call(this);\r
47332     }\r
47333 });\r
47334 Ext.reg('quicktip', Ext.QuickTip);/**\r
47335  * @class Ext.QuickTips\r
47336  * <p>Provides attractive and customizable tooltips for any element. The QuickTips\r
47337  * singleton is used to configure and manage tooltips globally for multiple elements\r
47338  * in a generic manner.  To create individual tooltips with maximum customizability,\r
47339  * you should consider either {@link Ext.Tip} or {@link Ext.ToolTip}.</p>\r
47340  * <p>Quicktips can be configured via tag attributes directly in markup, or by\r
47341  * registering quick tips programmatically via the {@link #register} method.</p>\r
47342  * <p>The singleton's instance of {@link Ext.QuickTip} is available via\r
47343  * {@link #getQuickTip}, and supports all the methods, and all the all the\r
47344  * configuration properties of Ext.QuickTip. These settings will apply to all\r
47345  * tooltips shown by the singleton.</p>\r
47346  * <p>Below is the summary of the configuration properties which can be used.\r
47347  * For detailed descriptions see {@link #getQuickTip}</p>\r
47348  * <p><b>QuickTips singleton configs (all are optional)</b></p>\r
47349  * <div class="mdetail-params"><ul><li>dismissDelay</li>\r
47350  * <li>hideDelay</li>\r
47351  * <li>maxWidth</li>\r
47352  * <li>minWidth</li>\r
47353  * <li>showDelay</li>\r
47354  * <li>trackMouse</li></ul></div>\r
47355  * <p><b>Target element configs (optional unless otherwise noted)</b></p>\r
47356  * <div class="mdetail-params"><ul><li>autoHide</li>\r
47357  * <li>cls</li>\r
47358  * <li>dismissDelay (overrides singleton value)</li>\r
47359  * <li>target (required)</li>\r
47360  * <li>text (required)</li>\r
47361  * <li>title</li>\r
47362  * <li>width</li></ul></div>\r
47363  * <p>Here is an example showing how some of these config options could be used:</p>\r
47364  * <pre><code>\r
47365 // Init the singleton.  Any tag-based quick tips will start working.\r
47366 Ext.QuickTips.init();\r
47367 \r
47368 // Apply a set of config properties to the singleton\r
47369 Ext.apply(Ext.QuickTips.getQuickTip(), {\r
47370     maxWidth: 200,\r
47371     minWidth: 100,\r
47372     showDelay: 50,\r
47373     trackMouse: true\r
47374 });\r
47375 \r
47376 // Manually register a quick tip for a specific element\r
47377 Ext.QuickTips.register({\r
47378     target: 'my-div',\r
47379     title: 'My Tooltip',\r
47380     text: 'This tooltip was added in code',\r
47381     width: 100,\r
47382     dismissDelay: 20\r
47383 });\r
47384 </code></pre>\r
47385  * <p>To register a quick tip in markup, you simply add one or more of the valid QuickTip attributes prefixed with\r
47386  * the <b>ext:</b> namespace.  The HTML element itself is automatically set as the quick tip target. Here is the summary\r
47387  * of supported attributes (optional unless otherwise noted):</p>\r
47388  * <ul><li><b>hide</b>: Specifying "user" is equivalent to setting autoHide = false.  Any other value will be the\r
47389  * same as autoHide = true.</li>\r
47390  * <li><b>qclass</b>: A CSS class to be applied to the quick tip (equivalent to the 'cls' target element config).</li>\r
47391  * <li><b>qtip (required)</b>: The quick tip text (equivalent to the 'text' target element config).</li>\r
47392  * <li><b>qtitle</b>: The quick tip title (equivalent to the 'title' target element config).</li>\r
47393  * <li><b>qwidth</b>: The quick tip width (equivalent to the 'width' target element config).</li></ul>\r
47394  * <p>Here is an example of configuring an HTML element to display a tooltip from markup:</p>\r
47395  * <pre><code>\r
47396 // Add a quick tip to an HTML button\r
47397 &lt;input type="button" value="OK" ext:qtitle="OK Button" ext:qwidth="100"\r
47398      ext:qtip="This is a quick tip from markup!">&lt;/input>\r
47399 </code></pre>\r
47400  * @singleton\r
47401  */\r
47402 Ext.QuickTips = function(){\r
47403     var tip, locks = [];\r
47404     return {\r
47405         /**\r
47406          * Initialize the global QuickTips instance and prepare any quick tips.\r
47407          * @param {Boolean} autoRender True to render the QuickTips container immediately to preload images. (Defaults to true) \r
47408          */\r
47409         init : function(autoRender){\r
47410             if(!tip){\r
47411                 if(!Ext.isReady){\r
47412                     Ext.onReady(function(){\r
47413                         Ext.QuickTips.init(autoRender);\r
47414                     });\r
47415                     return;\r
47416                 }\r
47417                 tip = new Ext.QuickTip({elements:'header,body'});\r
47418                 if(autoRender !== false){\r
47419                     tip.render(Ext.getBody());\r
47420                 }\r
47421             }\r
47422         },\r
47423 \r
47424         /**\r
47425          * Enable quick tips globally.\r
47426          */\r
47427         enable : function(){\r
47428             if(tip){\r
47429                 locks.pop();\r
47430                 if(locks.length < 1){\r
47431                     tip.enable();\r
47432                 }\r
47433             }\r
47434         },\r
47435 \r
47436         /**\r
47437          * Disable quick tips globally.\r
47438          */\r
47439         disable : function(){\r
47440             if(tip){\r
47441                 tip.disable();\r
47442             }\r
47443             locks.push(1);\r
47444         },\r
47445 \r
47446         /**\r
47447          * Returns true if quick tips are enabled, else false.\r
47448          * @return {Boolean}\r
47449          */\r
47450         isEnabled : function(){\r
47451             return tip !== undefined && !tip.disabled;\r
47452         },\r
47453 \r
47454         /**\r
47455          * Gets the global QuickTips instance.\r
47456          */\r
47457         getQuickTip : function(){\r
47458             return tip;\r
47459         },\r
47460 \r
47461         /**\r
47462          * Configures a new quick tip instance and assigns it to a target element.  See\r
47463          * {@link Ext.QuickTip#register} for details.\r
47464          * @param {Object} config The config object\r
47465          */\r
47466         register : function(){\r
47467             tip.register.apply(tip, arguments);\r
47468         },\r
47469 \r
47470         /**\r
47471          * Removes any registered quick tip from the target element and destroys it.\r
47472          * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.\r
47473          */\r
47474         unregister : function(){\r
47475             tip.unregister.apply(tip, arguments);\r
47476         },\r
47477 \r
47478         /**\r
47479          * Alias of {@link #register}.\r
47480          * @param {Object} config The config object\r
47481          */\r
47482         tips :function(){\r
47483             tip.register.apply(tip, arguments);\r
47484         }\r
47485     }\r
47486 }();/**\r
47487  * @class Ext.tree.TreePanel\r
47488  * @extends Ext.Panel\r
47489  * <p>The TreePanel provides tree-structured UI representation of tree-structured data.</p>\r
47490  * <p>{@link Ext.tree.TreeNode TreeNode}s added to the TreePanel may each contain metadata\r
47491  * used by your application in their {@link Ext.tree.TreeNode#attributes attributes} property.</p>\r
47492  * <p><b>A TreePanel must have a {@link #root} node before it is rendered.</b> This may either be\r
47493  * specified using the {@link #root} config option, or using the {@link #setRootNode} method.\r
47494  * <p>An example of tree rendered to an existing div:</p><pre><code>\r
47495 var tree = new Ext.tree.TreePanel({\r
47496     renderTo: 'tree-div',\r
47497     useArrows: true,\r
47498     autoScroll: true,\r
47499     animate: true,\r
47500     enableDD: true,\r
47501     containerScroll: true,\r
47502     border: false,\r
47503     // auto create TreeLoader\r
47504     dataUrl: 'get-nodes.php',\r
47505 \r
47506     root: {\r
47507         nodeType: 'async',\r
47508         text: 'Ext JS',\r
47509         draggable: false,\r
47510         id: 'source'\r
47511     }\r
47512 });\r
47513 \r
47514 tree.getRootNode().expand();\r
47515  * </code></pre>\r
47516  * <p>The example above would work with a data packet similar to this:</p><pre><code>\r
47517 [{\r
47518     "text": "adapter",\r
47519     "id": "source\/adapter",\r
47520     "cls": "folder"\r
47521 }, {\r
47522     "text": "dd",\r
47523     "id": "source\/dd",\r
47524     "cls": "folder"\r
47525 }, {\r
47526     "text": "debug.js",\r
47527     "id": "source\/debug.js",\r
47528     "leaf": true,\r
47529     "cls": "file"\r
47530 }]\r
47531  * </code></pre>\r
47532  * <p>An example of tree within a Viewport:</p><pre><code>\r
47533 new Ext.Viewport({\r
47534     layout: 'border',\r
47535     items: [{\r
47536         region: 'west',\r
47537         collapsible: true,\r
47538         title: 'Navigation',\r
47539         xtype: 'treepanel',\r
47540         width: 200,\r
47541         autoScroll: true,\r
47542         split: true,\r
47543         loader: new Ext.tree.TreeLoader(),\r
47544         root: new Ext.tree.AsyncTreeNode({\r
47545             expanded: true,\r
47546             children: [{\r
47547                 text: 'Menu Option 1',\r
47548                 leaf: true\r
47549             }, {\r
47550                 text: 'Menu Option 2',\r
47551                 leaf: true\r
47552             }, {\r
47553                 text: 'Menu Option 3',\r
47554                 leaf: true\r
47555             }]\r
47556         }),\r
47557         rootVisible: false,\r
47558         listeners: {\r
47559             click: function(n) {\r
47560                 Ext.Msg.alert('Navigation Tree Click', 'You clicked: "' + n.attributes.text + '"');\r
47561             }\r
47562         }\r
47563     }, {\r
47564         region: 'center',\r
47565         xtype: 'tabpanel',\r
47566         // remaining code not shown ...\r
47567     }]\r
47568 });\r
47569 </code></pre>\r
47570  *\r
47571  * @cfg {Ext.tree.TreeNode} root The root node for the tree.\r
47572  * @cfg {Boolean} rootVisible <tt>false</tt> to hide the root node (defaults to <tt>true</tt>)\r
47573  * @cfg {Boolean} lines <tt>false</tt> to disable tree lines (defaults to <tt>true</tt>)\r
47574  * @cfg {Boolean} enableDD <tt>true</tt> to enable drag and drop\r
47575  * @cfg {Boolean} enableDrag <tt>true</tt> to enable just drag\r
47576  * @cfg {Boolean} enableDrop <tt>true</tt> to enable just drop\r
47577  * @cfg {Object} dragConfig Custom config to pass to the {@link Ext.tree.TreeDragZone} instance\r
47578  * @cfg {Object} dropConfig Custom config to pass to the {@link Ext.tree.TreeDropZone} instance\r
47579  * @cfg {String} ddGroup The DD group this TreePanel belongs to\r
47580  * @cfg {Boolean} ddAppendOnly <tt>true</tt> if the tree should only allow append drops (use for trees which are sorted)\r
47581  * @cfg {Boolean} ddScroll <tt>true</tt> to enable body scrolling\r
47582  * @cfg {Boolean} containerScroll <tt>true</tt> to register this container with ScrollManager\r
47583  * @cfg {Boolean} hlDrop <tt>false</tt> to disable node highlight on drop (defaults to the value of {@link Ext#enableFx})\r
47584  * @cfg {String} hlColor The color of the node highlight (defaults to <tt>'C3DAF9'</tt>)\r
47585  * @cfg {Boolean} animate <tt>true</tt> to enable animated expand/collapse (defaults to the value of {@link Ext#enableFx})\r
47586  * @cfg {Boolean} singleExpand <tt>true</tt> if only 1 node per branch may be expanded\r
47587  * @cfg {Object} selModel A tree selection model to use with this TreePanel (defaults to an {@link Ext.tree.DefaultSelectionModel})\r
47588  * @cfg {Boolean} trackMouseOver <tt>false</tt> to disable mouse over highlighting\r
47589  * @cfg {Ext.tree.TreeLoader} loader A {@link Ext.tree.TreeLoader} for use with this TreePanel\r
47590  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to <tt>'/'</tt>)\r
47591  * @cfg {Boolean} useArrows <tt>true</tt> to use Vista-style arrows in the tree (defaults to <tt>false</tt>)\r
47592  * @cfg {String} requestMethod The HTTP request method for loading data (defaults to the value of {@link Ext.Ajax#method}).\r
47593  *\r
47594  * @constructor\r
47595  * @param {Object} config\r
47596  * @xtype treepanel\r
47597  */\r
47598 Ext.tree.TreePanel = Ext.extend(Ext.Panel, {\r
47599     rootVisible : true,\r
47600     animate : Ext.enableFx,\r
47601     lines : true,\r
47602     enableDD : false,\r
47603     hlDrop : Ext.enableFx,\r
47604     pathSeparator : '/',\r
47605 \r
47606     /**\r
47607      * @cfg {Array} bubbleEvents\r
47608      * <p>An array of events that, when fired, should be bubbled to any parent container.\r
47609      * See {@link Ext.util.Observable#enableBubble}.\r
47610      * Defaults to <tt>[]</tt>.\r
47611      */\r
47612     bubbleEvents : [],\r
47613 \r
47614     initComponent : function(){\r
47615         Ext.tree.TreePanel.superclass.initComponent.call(this);\r
47616 \r
47617         if(!this.eventModel){\r
47618             this.eventModel = new Ext.tree.TreeEventModel(this);\r
47619         }\r
47620 \r
47621         // initialize the loader\r
47622         var l = this.loader;\r
47623         if(!l){\r
47624             l = new Ext.tree.TreeLoader({\r
47625                 dataUrl: this.dataUrl,\r
47626                 requestMethod: this.requestMethod\r
47627             });\r
47628         }else if(Ext.isObject(l) && !l.load){\r
47629             l = new Ext.tree.TreeLoader(l);\r
47630         }\r
47631         this.loader = l;\r
47632 \r
47633         this.nodeHash = {};\r
47634 \r
47635         /**\r
47636         * The root node of this tree.\r
47637         * @type Ext.tree.TreeNode\r
47638         * @property root\r
47639         */\r
47640         if(this.root){\r
47641             var r = this.root;\r
47642             delete this.root;\r
47643             this.setRootNode(r);\r
47644         }\r
47645 \r
47646 \r
47647         this.addEvents(\r
47648 \r
47649             /**\r
47650             * @event append\r
47651             * Fires when a new child node is appended to a node in this tree.\r
47652             * @param {Tree} tree The owner tree\r
47653             * @param {Node} parent The parent node\r
47654             * @param {Node} node The newly appended node\r
47655             * @param {Number} index The index of the newly appended node\r
47656             */\r
47657            'append',\r
47658            /**\r
47659             * @event remove\r
47660             * Fires when a child node is removed from a node in this tree.\r
47661             * @param {Tree} tree The owner tree\r
47662             * @param {Node} parent The parent node\r
47663             * @param {Node} node The child node removed\r
47664             */\r
47665            'remove',\r
47666            /**\r
47667             * @event movenode\r
47668             * Fires when a node is moved to a new location in the tree\r
47669             * @param {Tree} tree The owner tree\r
47670             * @param {Node} node The node moved\r
47671             * @param {Node} oldParent The old parent of this node\r
47672             * @param {Node} newParent The new parent of this node\r
47673             * @param {Number} index The index it was moved to\r
47674             */\r
47675            'movenode',\r
47676            /**\r
47677             * @event insert\r
47678             * Fires when a new child node is inserted in a node in this tree.\r
47679             * @param {Tree} tree The owner tree\r
47680             * @param {Node} parent The parent node\r
47681             * @param {Node} node The child node inserted\r
47682             * @param {Node} refNode The child node the node was inserted before\r
47683             */\r
47684            'insert',\r
47685            /**\r
47686             * @event beforeappend\r
47687             * Fires before a new child is appended to a node in this tree, return false to cancel the append.\r
47688             * @param {Tree} tree The owner tree\r
47689             * @param {Node} parent The parent node\r
47690             * @param {Node} node The child node to be appended\r
47691             */\r
47692            'beforeappend',\r
47693            /**\r
47694             * @event beforeremove\r
47695             * Fires before a child is removed from a node in this tree, return false to cancel the remove.\r
47696             * @param {Tree} tree The owner tree\r
47697             * @param {Node} parent The parent node\r
47698             * @param {Node} node The child node to be removed\r
47699             */\r
47700            'beforeremove',\r
47701            /**\r
47702             * @event beforemovenode\r
47703             * Fires before a node is moved to a new location in the tree. Return false to cancel the move.\r
47704             * @param {Tree} tree The owner tree\r
47705             * @param {Node} node The node being moved\r
47706             * @param {Node} oldParent The parent of the node\r
47707             * @param {Node} newParent The new parent the node is moving to\r
47708             * @param {Number} index The index it is being moved to\r
47709             */\r
47710            'beforemovenode',\r
47711            /**\r
47712             * @event beforeinsert\r
47713             * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.\r
47714             * @param {Tree} tree The owner tree\r
47715             * @param {Node} parent The parent node\r
47716             * @param {Node} node The child node to be inserted\r
47717             * @param {Node} refNode The child node the node is being inserted before\r
47718             */\r
47719             'beforeinsert',\r
47720 \r
47721             /**\r
47722             * @event beforeload\r
47723             * Fires before a node is loaded, return false to cancel\r
47724             * @param {Node} node The node being loaded\r
47725             */\r
47726             'beforeload',\r
47727             /**\r
47728             * @event load\r
47729             * Fires when a node is loaded\r
47730             * @param {Node} node The node that was loaded\r
47731             */\r
47732             'load',\r
47733             /**\r
47734             * @event textchange\r
47735             * Fires when the text for a node is changed\r
47736             * @param {Node} node The node\r
47737             * @param {String} text The new text\r
47738             * @param {String} oldText The old text\r
47739             */\r
47740             'textchange',\r
47741             /**\r
47742             * @event beforeexpandnode\r
47743             * Fires before a node is expanded, return false to cancel.\r
47744             * @param {Node} node The node\r
47745             * @param {Boolean} deep\r
47746             * @param {Boolean} anim\r
47747             */\r
47748             'beforeexpandnode',\r
47749             /**\r
47750             * @event beforecollapsenode\r
47751             * Fires before a node is collapsed, return false to cancel.\r
47752             * @param {Node} node The node\r
47753             * @param {Boolean} deep\r
47754             * @param {Boolean} anim\r
47755             */\r
47756             'beforecollapsenode',\r
47757             /**\r
47758             * @event expandnode\r
47759             * Fires when a node is expanded\r
47760             * @param {Node} node The node\r
47761             */\r
47762             'expandnode',\r
47763             /**\r
47764             * @event disabledchange\r
47765             * Fires when the disabled status of a node changes\r
47766             * @param {Node} node The node\r
47767             * @param {Boolean} disabled\r
47768             */\r
47769             'disabledchange',\r
47770             /**\r
47771             * @event collapsenode\r
47772             * Fires when a node is collapsed\r
47773             * @param {Node} node The node\r
47774             */\r
47775             'collapsenode',\r
47776             /**\r
47777             * @event beforeclick\r
47778             * Fires before click processing on a node. Return false to cancel the default action.\r
47779             * @param {Node} node The node\r
47780             * @param {Ext.EventObject} e The event object\r
47781             */\r
47782             'beforeclick',\r
47783             /**\r
47784             * @event click\r
47785             * Fires when a node is clicked\r
47786             * @param {Node} node The node\r
47787             * @param {Ext.EventObject} e The event object\r
47788             */\r
47789             'click',\r
47790             /**\r
47791             * @event containerclick\r
47792             * Fires when the tree container is clicked\r
47793             * @param {Tree} this\r
47794             * @param {Ext.EventObject} e The event object\r
47795             */\r
47796             'containerclick',\r
47797             /**\r
47798             * @event checkchange\r
47799             * Fires when a node with a checkbox's checked property changes\r
47800             * @param {Node} this This node\r
47801             * @param {Boolean} checked\r
47802             */\r
47803             'checkchange',\r
47804             /**\r
47805             * @event beforedblclick\r
47806             * Fires before double click processing on a node. Return false to cancel the default action.\r
47807             * @param {Node} node The node\r
47808             * @param {Ext.EventObject} e The event object\r
47809             */\r
47810             'beforedblclick',\r
47811             /**\r
47812             * @event dblclick\r
47813             * Fires when a node is double clicked\r
47814             * @param {Node} node The node\r
47815             * @param {Ext.EventObject} e The event object\r
47816             */\r
47817             'dblclick',\r
47818             /**\r
47819             * @event containerdblclick\r
47820             * Fires when the tree container is double clicked\r
47821             * @param {Tree} this\r
47822             * @param {Ext.EventObject} e The event object\r
47823             */\r
47824             'containerdblclick',\r
47825             /**\r
47826             * @event contextmenu\r
47827             * Fires when a node is right clicked. To display a context menu in response to this\r
47828             * event, first create a Menu object (see {@link Ext.menu.Menu} for details), then add\r
47829             * a handler for this event:<pre><code>\r
47830 new Ext.tree.TreePanel({\r
47831     title: 'My TreePanel',\r
47832     root: new Ext.tree.AsyncTreeNode({\r
47833         text: 'The Root',\r
47834         children: [\r
47835             { text: 'Child node 1', leaf: true },\r
47836             { text: 'Child node 2', leaf: true }\r
47837         ]\r
47838     }),\r
47839     contextMenu: new Ext.menu.Menu({\r
47840         items: [{\r
47841             id: 'delete-node',\r
47842             text: 'Delete Node'\r
47843         }],\r
47844         listeners: {\r
47845             itemclick: function(item) {\r
47846                 switch (item.id) {\r
47847                     case 'delete-node':\r
47848                         var n = item.parentMenu.contextNode;\r
47849                         if (n.parentNode) {\r
47850                             n.remove();\r
47851                         }\r
47852                         break;\r
47853                 }\r
47854             }\r
47855         }\r
47856     }),\r
47857     listeners: {\r
47858         contextmenu: function(node, e) {\r
47859 //          Register the context node with the menu so that a Menu Item's handler function can access\r
47860 //          it via its {@link Ext.menu.BaseItem#parentMenu parentMenu} property.\r
47861             node.select();\r
47862             var c = node.getOwnerTree().contextMenu;\r
47863             c.contextNode = node;\r
47864             c.showAt(e.getXY());\r
47865         }\r
47866     }\r
47867 });\r
47868 </code></pre>\r
47869             * @param {Node} node The node\r
47870             * @param {Ext.EventObject} e The event object\r
47871             */\r
47872             'contextmenu',\r
47873             /**\r
47874             * @event containercontextmenu\r
47875             * Fires when the tree container is right clicked\r
47876             * @param {Tree} this\r
47877             * @param {Ext.EventObject} e The event object\r
47878             */\r
47879             'containercontextmenu',\r
47880             /**\r
47881             * @event beforechildrenrendered\r
47882             * Fires right before the child nodes for a node are rendered\r
47883             * @param {Node} node The node\r
47884             */\r
47885             'beforechildrenrendered',\r
47886            /**\r
47887              * @event startdrag\r
47888              * Fires when a node starts being dragged\r
47889              * @param {Ext.tree.TreePanel} this\r
47890              * @param {Ext.tree.TreeNode} node\r
47891              * @param {event} e The raw browser event\r
47892              */\r
47893             'startdrag',\r
47894             /**\r
47895              * @event enddrag\r
47896              * Fires when a drag operation is complete\r
47897              * @param {Ext.tree.TreePanel} this\r
47898              * @param {Ext.tree.TreeNode} node\r
47899              * @param {event} e The raw browser event\r
47900              */\r
47901             'enddrag',\r
47902             /**\r
47903              * @event dragdrop\r
47904              * Fires when a dragged node is dropped on a valid DD target\r
47905              * @param {Ext.tree.TreePanel} this\r
47906              * @param {Ext.tree.TreeNode} node\r
47907              * @param {DD} dd The dd it was dropped on\r
47908              * @param {event} e The raw browser event\r
47909              */\r
47910             'dragdrop',\r
47911             /**\r
47912              * @event beforenodedrop\r
47913              * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent\r
47914              * passed to handlers has the following properties:<br />\r
47915              * <ul style="padding:5px;padding-left:16px;">\r
47916              * <li>tree - The TreePanel</li>\r
47917              * <li>target - The node being targeted for the drop</li>\r
47918              * <li>data - The drag data from the drag source</li>\r
47919              * <li>point - The point of the drop - append, above or below</li>\r
47920              * <li>source - The drag source</li>\r
47921              * <li>rawEvent - Raw mouse event</li>\r
47922              * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)\r
47923              * to be inserted by setting them on this object.</li>\r
47924              * <li>cancel - Set this to true to cancel the drop.</li>\r
47925              * <li>dropStatus - If the default drop action is cancelled but the drop is valid, setting this to true\r
47926              * will prevent the animated 'repair' from appearing.</li>\r
47927              * </ul>\r
47928              * @param {Object} dropEvent\r
47929              */\r
47930             'beforenodedrop',\r
47931             /**\r
47932              * @event nodedrop\r
47933              * Fires after a DD object is dropped on a node in this tree. The dropEvent\r
47934              * passed to handlers has the following properties:<br />\r
47935              * <ul style="padding:5px;padding-left:16px;">\r
47936              * <li>tree - The TreePanel</li>\r
47937              * <li>target - The node being targeted for the drop</li>\r
47938              * <li>data - The drag data from the drag source</li>\r
47939              * <li>point - The point of the drop - append, above or below</li>\r
47940              * <li>source - The drag source</li>\r
47941              * <li>rawEvent - Raw mouse event</li>\r
47942              * <li>dropNode - Dropped node(s).</li>\r
47943              * </ul>\r
47944              * @param {Object} dropEvent\r
47945              */\r
47946             'nodedrop',\r
47947              /**\r
47948              * @event nodedragover\r
47949              * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent\r
47950              * passed to handlers has the following properties:<br />\r
47951              * <ul style="padding:5px;padding-left:16px;">\r
47952              * <li>tree - The TreePanel</li>\r
47953              * <li>target - The node being targeted for the drop</li>\r
47954              * <li>data - The drag data from the drag source</li>\r
47955              * <li>point - The point of the drop - append, above or below</li>\r
47956              * <li>source - The drag source</li>\r
47957              * <li>rawEvent - Raw mouse event</li>\r
47958              * <li>dropNode - Drop node(s) provided by the source.</li>\r
47959              * <li>cancel - Set this to true to signal drop not allowed.</li>\r
47960              * </ul>\r
47961              * @param {Object} dragOverEvent\r
47962              */\r
47963             'nodedragover'\r
47964         );\r
47965         if(this.singleExpand){\r
47966             this.on('beforeexpandnode', this.restrictExpand, this);\r
47967         }\r
47968     },\r
47969 \r
47970     // private\r
47971     proxyNodeEvent : function(ename, a1, a2, a3, a4, a5, a6){\r
47972         if(ename == 'collapse' || ename == 'expand' || ename == 'beforecollapse' || ename == 'beforeexpand' || ename == 'move' || ename == 'beforemove'){\r
47973             ename = ename+'node';\r
47974         }\r
47975         // args inline for performance while bubbling events\r
47976         return this.fireEvent(ename, a1, a2, a3, a4, a5, a6);\r
47977     },\r
47978 \r
47979 \r
47980     /**\r
47981      * Returns this root node for this tree\r
47982      * @return {Node}\r
47983      */\r
47984     getRootNode : function(){\r
47985         return this.root;\r
47986     },\r
47987 \r
47988     /**\r
47989      * Sets the root node for this tree. If the TreePanel has already rendered a root node, the\r
47990      * previous root node (and all of its descendants) are destroyed before the new root node is rendered.\r
47991      * @param {Node} node\r
47992      * @return {Node}\r
47993      */\r
47994     setRootNode : function(node){\r
47995         Ext.destroy(this.root);\r
47996         if(!node.render){ // attributes passed\r
47997             node = this.loader.createNode(node);\r
47998         }\r
47999         this.root = node;\r
48000         node.ownerTree = this;\r
48001         node.isRoot = true;\r
48002         this.registerNode(node);\r
48003         if(!this.rootVisible){\r
48004             var uiP = node.attributes.uiProvider;\r
48005             node.ui = uiP ? new uiP(node) : new Ext.tree.RootTreeNodeUI(node);\r
48006         }\r
48007         if(this.innerCt){\r
48008             this.clearInnerCt();\r
48009             this.renderRoot();\r
48010         }\r
48011         return node;\r
48012     },\r
48013     \r
48014     clearInnerCt : function(){\r
48015         this.innerCt.update('');    \r
48016     },\r
48017     \r
48018     // private\r
48019     renderRoot : function(){\r
48020         this.root.render();\r
48021         if(!this.rootVisible){\r
48022             this.root.renderChildren();\r
48023         }\r
48024     },\r
48025 \r
48026     /**\r
48027      * Gets a node in this tree by its id\r
48028      * @param {String} id\r
48029      * @return {Node}\r
48030      */\r
48031     getNodeById : function(id){\r
48032         return this.nodeHash[id];\r
48033     },\r
48034 \r
48035     // private\r
48036     registerNode : function(node){\r
48037         this.nodeHash[node.id] = node;\r
48038     },\r
48039 \r
48040     // private\r
48041     unregisterNode : function(node){\r
48042         delete this.nodeHash[node.id];\r
48043     },\r
48044 \r
48045     // private\r
48046     toString : function(){\r
48047         return '[Tree'+(this.id?' '+this.id:'')+']';\r
48048     },\r
48049 \r
48050     // private\r
48051     restrictExpand : function(node){\r
48052         var p = node.parentNode;\r
48053         if(p){\r
48054             if(p.expandedChild && p.expandedChild.parentNode == p){\r
48055                 p.expandedChild.collapse();\r
48056             }\r
48057             p.expandedChild = node;\r
48058         }\r
48059     },\r
48060 \r
48061     /**\r
48062      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. 'id')\r
48063      * @param {String} attribute (optional) Defaults to null (return the actual nodes)\r
48064      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root\r
48065      * @return {Array}\r
48066      */\r
48067     getChecked : function(a, startNode){\r
48068         startNode = startNode || this.root;\r
48069         var r = [];\r
48070         var f = function(){\r
48071             if(this.attributes.checked){\r
48072                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));\r
48073             }\r
48074         };\r
48075         startNode.cascade(f);\r
48076         return r;\r
48077     },\r
48078 \r
48079     /**\r
48080      * Returns the default {@link Ext.tree.TreeLoader} for this TreePanel.\r
48081      * @return {Ext.tree.TreeLoader} The TreeLoader for this TreePanel.\r
48082      */\r
48083     getLoader : function(){\r
48084         return this.loader;\r
48085     },\r
48086 \r
48087     /**\r
48088      * Expand all nodes\r
48089      */\r
48090     expandAll : function(){\r
48091         this.root.expand(true);\r
48092     },\r
48093 \r
48094     /**\r
48095      * Collapse all nodes\r
48096      */\r
48097     collapseAll : function(){\r
48098         this.root.collapse(true);\r
48099     },\r
48100 \r
48101     /**\r
48102      * Returns the selection model used by this TreePanel.\r
48103      * @return {TreeSelectionModel} The selection model used by this TreePanel\r
48104      */\r
48105     getSelectionModel : function(){\r
48106         if(!this.selModel){\r
48107             this.selModel = new Ext.tree.DefaultSelectionModel();\r
48108         }\r
48109         return this.selModel;\r
48110     },\r
48111 \r
48112     /**\r
48113      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Ext.data.Node#getPath}\r
48114      * @param {String} path\r
48115      * @param {String} attr (optional) The attribute used in the path (see {@link Ext.data.Node#getPath} for more info)\r
48116      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with\r
48117      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.\r
48118      */\r
48119     expandPath : function(path, attr, callback){\r
48120         attr = attr || 'id';\r
48121         var keys = path.split(this.pathSeparator);\r
48122         var curNode = this.root;\r
48123         if(curNode.attributes[attr] != keys[1]){ // invalid root\r
48124             if(callback){\r
48125                 callback(false, null);\r
48126             }\r
48127             return;\r
48128         }\r
48129         var index = 1;\r
48130         var f = function(){\r
48131             if(++index == keys.length){\r
48132                 if(callback){\r
48133                     callback(true, curNode);\r
48134                 }\r
48135                 return;\r
48136             }\r
48137             var c = curNode.findChild(attr, keys[index]);\r
48138             if(!c){\r
48139                 if(callback){\r
48140                     callback(false, curNode);\r
48141                 }\r
48142                 return;\r
48143             }\r
48144             curNode = c;\r
48145             c.expand(false, false, f);\r
48146         };\r
48147         curNode.expand(false, false, f);\r
48148     },\r
48149 \r
48150     /**\r
48151      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Ext.data.Node#getPath}\r
48152      * @param {String} path\r
48153      * @param {String} attr (optional) The attribute used in the path (see {@link Ext.data.Node#getPath} for more info)\r
48154      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with\r
48155      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.\r
48156      */\r
48157     selectPath : function(path, attr, callback){\r
48158         attr = attr || 'id';\r
48159         var keys = path.split(this.pathSeparator),\r
48160             v = keys.pop();\r
48161         if(keys.length > 1){\r
48162             var f = function(success, node){\r
48163                 if(success && node){\r
48164                     var n = node.findChild(attr, v);\r
48165                     if(n){\r
48166                         n.select();\r
48167                         if(callback){\r
48168                             callback(true, n);\r
48169                         }\r
48170                     }else if(callback){\r
48171                         callback(false, n);\r
48172                     }\r
48173                 }else{\r
48174                     if(callback){\r
48175                         callback(false, n);\r
48176                     }\r
48177                 }\r
48178             };\r
48179             this.expandPath(keys.join(this.pathSeparator), attr, f);\r
48180         }else{\r
48181             this.root.select();\r
48182             if(callback){\r
48183                 callback(true, this.root);\r
48184             }\r
48185         }\r
48186     },\r
48187 \r
48188     /**\r
48189      * Returns the underlying Element for this tree\r
48190      * @return {Ext.Element} The Element\r
48191      */\r
48192     getTreeEl : function(){\r
48193         return this.body;\r
48194     },\r
48195 \r
48196     // private\r
48197     onRender : function(ct, position){\r
48198         Ext.tree.TreePanel.superclass.onRender.call(this, ct, position);\r
48199         this.el.addClass('x-tree');\r
48200         this.innerCt = this.body.createChild({tag:'ul',\r
48201                cls:'x-tree-root-ct ' +\r
48202                (this.useArrows ? 'x-tree-arrows' : this.lines ? 'x-tree-lines' : 'x-tree-no-lines')});\r
48203     },\r
48204 \r
48205     // private\r
48206     initEvents : function(){\r
48207         Ext.tree.TreePanel.superclass.initEvents.call(this);\r
48208 \r
48209         if(this.containerScroll){\r
48210             Ext.dd.ScrollManager.register(this.body);\r
48211         }\r
48212         if((this.enableDD || this.enableDrop) && !this.dropZone){\r
48213            /**\r
48214             * The dropZone used by this tree if drop is enabled (see {@link #enableDD} or {@link #enableDrop})\r
48215             * @property dropZone\r
48216             * @type Ext.tree.TreeDropZone\r
48217             */\r
48218              this.dropZone = new Ext.tree.TreeDropZone(this, this.dropConfig || {\r
48219                ddGroup: this.ddGroup || 'TreeDD', appendOnly: this.ddAppendOnly === true\r
48220            });\r
48221         }\r
48222         if((this.enableDD || this.enableDrag) && !this.dragZone){\r
48223            /**\r
48224             * The dragZone used by this tree if drag is enabled (see {@link #enableDD} or {@link #enableDrag})\r
48225             * @property dragZone\r
48226             * @type Ext.tree.TreeDragZone\r
48227             */\r
48228             this.dragZone = new Ext.tree.TreeDragZone(this, this.dragConfig || {\r
48229                ddGroup: this.ddGroup || 'TreeDD',\r
48230                scroll: this.ddScroll\r
48231            });\r
48232         }\r
48233         this.getSelectionModel().init(this);\r
48234     },\r
48235 \r
48236     // private\r
48237     afterRender : function(){\r
48238         Ext.tree.TreePanel.superclass.afterRender.call(this);\r
48239         this.renderRoot();\r
48240     },\r
48241 \r
48242     beforeDestroy : function(){\r
48243         if(this.rendered){\r
48244             Ext.dd.ScrollManager.unregister(this.body);\r
48245             Ext.destroy(this.dropZone, this.dragZone);\r
48246         }\r
48247         Ext.destroy(this.root, this.loader);\r
48248         this.nodeHash = this.root = this.loader = null;\r
48249         Ext.tree.TreePanel.superclass.beforeDestroy.call(this);\r
48250     }\r
48251 \r
48252     /**\r
48253      * @cfg {String/Number} activeItem\r
48254      * @hide\r
48255      */\r
48256     /**\r
48257      * @cfg {Boolean} autoDestroy\r
48258      * @hide\r
48259      */\r
48260     /**\r
48261      * @cfg {Object/String/Function} autoLoad\r
48262      * @hide\r
48263      */\r
48264     /**\r
48265      * @cfg {Boolean} autoWidth\r
48266      * @hide\r
48267      */\r
48268     /**\r
48269      * @cfg {Boolean/Number} bufferResize\r
48270      * @hide\r
48271      */\r
48272     /**\r
48273      * @cfg {String} defaultType\r
48274      * @hide\r
48275      */\r
48276     /**\r
48277      * @cfg {Object} defaults\r
48278      * @hide\r
48279      */\r
48280     /**\r
48281      * @cfg {Boolean} hideBorders\r
48282      * @hide\r
48283      */\r
48284     /**\r
48285      * @cfg {Mixed} items\r
48286      * @hide\r
48287      */\r
48288     /**\r
48289      * @cfg {String} layout\r
48290      * @hide\r
48291      */\r
48292     /**\r
48293      * @cfg {Object} layoutConfig\r
48294      * @hide\r
48295      */\r
48296     /**\r
48297      * @cfg {Boolean} monitorResize\r
48298      * @hide\r
48299      */\r
48300     /**\r
48301      * @property items\r
48302      * @hide\r
48303      */\r
48304     /**\r
48305      * @method cascade\r
48306      * @hide\r
48307      */\r
48308     /**\r
48309      * @method doLayout\r
48310      * @hide\r
48311      */\r
48312     /**\r
48313      * @method find\r
48314      * @hide\r
48315      */\r
48316     /**\r
48317      * @method findBy\r
48318      * @hide\r
48319      */\r
48320     /**\r
48321      * @method findById\r
48322      * @hide\r
48323      */\r
48324     /**\r
48325      * @method findByType\r
48326      * @hide\r
48327      */\r
48328     /**\r
48329      * @method getComponent\r
48330      * @hide\r
48331      */\r
48332     /**\r
48333      * @method getLayout\r
48334      * @hide\r
48335      */\r
48336     /**\r
48337      * @method getUpdater\r
48338      * @hide\r
48339      */\r
48340     /**\r
48341      * @method insert\r
48342      * @hide\r
48343      */\r
48344     /**\r
48345      * @method load\r
48346      * @hide\r
48347      */\r
48348     /**\r
48349      * @method remove\r
48350      * @hide\r
48351      */\r
48352     /**\r
48353      * @event add\r
48354      * @hide\r
48355      */\r
48356     /**\r
48357      * @method removeAll\r
48358      * @hide\r
48359      */\r
48360     /**\r
48361      * @event afterLayout\r
48362      * @hide\r
48363      */\r
48364     /**\r
48365      * @event beforeadd\r
48366      * @hide\r
48367      */\r
48368     /**\r
48369      * @event beforeremove\r
48370      * @hide\r
48371      */\r
48372     /**\r
48373      * @event remove\r
48374      * @hide\r
48375      */\r
48376 \r
48377 \r
48378 \r
48379     /**\r
48380      * @cfg {String} allowDomMove  @hide\r
48381      */\r
48382     /**\r
48383      * @cfg {String} autoEl @hide\r
48384      */\r
48385     /**\r
48386      * @cfg {String} applyTo  @hide\r
48387      */\r
48388     /**\r
48389      * @cfg {String} contentEl  @hide\r
48390      */\r
48391     /**\r
48392      * @cfg {Mixed} data  @hide\r
48393      */\r
48394     /**\r
48395      * @cfg {Mixed} tpl  @hide\r
48396      */\r
48397     /**\r
48398      * @cfg {String} tplWriteMode  @hide\r
48399      */\r
48400     /**\r
48401      * @cfg {String} disabledClass  @hide\r
48402      */\r
48403     /**\r
48404      * @cfg {String} elements  @hide\r
48405      */\r
48406     /**\r
48407      * @cfg {String} html  @hide\r
48408      */\r
48409     /**\r
48410      * @cfg {Boolean} preventBodyReset\r
48411      * @hide\r
48412      */\r
48413     /**\r
48414      * @property disabled\r
48415      * @hide\r
48416      */\r
48417     /**\r
48418      * @method applyToMarkup\r
48419      * @hide\r
48420      */\r
48421     /**\r
48422      * @method enable\r
48423      * @hide\r
48424      */\r
48425     /**\r
48426      * @method disable\r
48427      * @hide\r
48428      */\r
48429     /**\r
48430      * @method setDisabled\r
48431      * @hide\r
48432      */\r
48433 });\r
48434 \r
48435 Ext.tree.TreePanel.nodeTypes = {};\r
48436 \r
48437 Ext.reg('treepanel', Ext.tree.TreePanel);Ext.tree.TreeEventModel = function(tree){\r
48438     this.tree = tree;\r
48439     this.tree.on('render', this.initEvents, this);\r
48440 }\r
48441 \r
48442 Ext.tree.TreeEventModel.prototype = {\r
48443     initEvents : function(){\r
48444         var t = this.tree;\r
48445 \r
48446         if(t.trackMouseOver !== false){\r
48447             t.mon(t.innerCt, {\r
48448                 scope: this,\r
48449                 mouseover: this.delegateOver,\r
48450                 mouseout: this.delegateOut\r
48451             });\r
48452         }\r
48453         t.mon(t.getTreeEl(), {\r
48454             scope: this,\r
48455             click: this.delegateClick,\r
48456             dblclick: this.delegateDblClick,\r
48457             contextmenu: this.delegateContextMenu\r
48458         });\r
48459     },\r
48460 \r
48461     getNode : function(e){\r
48462         var t;\r
48463         if(t = e.getTarget('.x-tree-node-el', 10)){\r
48464             var id = Ext.fly(t, '_treeEvents').getAttribute('tree-node-id', 'ext');\r
48465             if(id){\r
48466                 return this.tree.getNodeById(id);\r
48467             }\r
48468         }\r
48469         return null;\r
48470     },\r
48471 \r
48472     getNodeTarget : function(e){\r
48473         var t = e.getTarget('.x-tree-node-icon', 1);\r
48474         if(!t){\r
48475             t = e.getTarget('.x-tree-node-el', 6);\r
48476         }\r
48477         return t;\r
48478     },\r
48479 \r
48480     delegateOut : function(e, t){\r
48481         if(!this.beforeEvent(e)){\r
48482             return;\r
48483         }\r
48484         if(e.getTarget('.x-tree-ec-icon', 1)){\r
48485             var n = this.getNode(e);\r
48486             this.onIconOut(e, n);\r
48487             if(n == this.lastEcOver){\r
48488                 delete this.lastEcOver;\r
48489             }\r
48490         }\r
48491         if((t = this.getNodeTarget(e)) && !e.within(t, true)){\r
48492             this.onNodeOut(e, this.getNode(e));\r
48493         }\r
48494     },\r
48495 \r
48496     delegateOver : function(e, t){\r
48497         if(!this.beforeEvent(e)){\r
48498             return;\r
48499         }\r
48500         if(Ext.isGecko && !this.trackingDoc){ // prevent hanging in FF\r
48501             Ext.getBody().on('mouseover', this.trackExit, this);\r
48502             this.trackingDoc = true;\r
48503         }\r
48504         if(this.lastEcOver){ // prevent hung highlight\r
48505             this.onIconOut(e, this.lastEcOver);\r
48506             delete this.lastEcOver;\r
48507         }\r
48508         if(e.getTarget('.x-tree-ec-icon', 1)){\r
48509             this.lastEcOver = this.getNode(e);\r
48510             this.onIconOver(e, this.lastEcOver);\r
48511         }\r
48512         if(t = this.getNodeTarget(e)){\r
48513             this.onNodeOver(e, this.getNode(e));\r
48514         }\r
48515     },\r
48516 \r
48517     trackExit : function(e){\r
48518         if(this.lastOverNode){\r
48519             if(this.lastOverNode.ui && !e.within(this.lastOverNode.ui.getEl())){\r
48520                 this.onNodeOut(e, this.lastOverNode);\r
48521             }\r
48522             delete this.lastOverNode;\r
48523             Ext.getBody().un('mouseover', this.trackExit, this);\r
48524             this.trackingDoc = false;\r
48525         }\r
48526 \r
48527     },\r
48528 \r
48529     delegateClick : function(e, t){\r
48530         if(this.beforeEvent(e)){\r
48531             if(e.getTarget('input[type=checkbox]', 1)){\r
48532                 this.onCheckboxClick(e, this.getNode(e));\r
48533             }else if(e.getTarget('.x-tree-ec-icon', 1)){\r
48534                 this.onIconClick(e, this.getNode(e));\r
48535             }else if(this.getNodeTarget(e)){\r
48536                 this.onNodeClick(e, this.getNode(e));\r
48537             }else{\r
48538                 this.onContainerEvent(e, 'click');\r
48539             }\r
48540         }\r
48541     },\r
48542 \r
48543     delegateDblClick : function(e, t){\r
48544         if(this.beforeEvent(e)){\r
48545             if(this.getNodeTarget(e)){\r
48546                 this.onNodeDblClick(e, this.getNode(e));\r
48547             }else{\r
48548                 this.onContainerEvent(e, 'dblclick');\r
48549             }\r
48550         }\r
48551     },\r
48552 \r
48553     delegateContextMenu : function(e, t){\r
48554         if(this.beforeEvent(e)){\r
48555             if(this.getNodeTarget(e)){\r
48556                 this.onNodeContextMenu(e, this.getNode(e));\r
48557             }else{\r
48558                 this.onContainerEvent(e, 'contextmenu');\r
48559             }\r
48560         }\r
48561     },\r
48562 \r
48563     onContainerEvent: function(e, type){\r
48564         this.tree.fireEvent('container' + type, this.tree, e);\r
48565     },\r
48566 \r
48567     onNodeClick : function(e, node){\r
48568         node.ui.onClick(e);\r
48569     },\r
48570 \r
48571     onNodeOver : function(e, node){\r
48572         this.lastOverNode = node;\r
48573         node.ui.onOver(e);\r
48574     },\r
48575 \r
48576     onNodeOut : function(e, node){\r
48577         node.ui.onOut(e);\r
48578     },\r
48579 \r
48580     onIconOver : function(e, node){\r
48581         node.ui.addClass('x-tree-ec-over');\r
48582     },\r
48583 \r
48584     onIconOut : function(e, node){\r
48585         node.ui.removeClass('x-tree-ec-over');\r
48586     },\r
48587 \r
48588     onIconClick : function(e, node){\r
48589         node.ui.ecClick(e);\r
48590     },\r
48591 \r
48592     onCheckboxClick : function(e, node){\r
48593         node.ui.onCheckChange(e);\r
48594     },\r
48595 \r
48596     onNodeDblClick : function(e, node){\r
48597         node.ui.onDblClick(e);\r
48598     },\r
48599 \r
48600     onNodeContextMenu : function(e, node){\r
48601         node.ui.onContextMenu(e);\r
48602     },\r
48603 \r
48604     beforeEvent : function(e){\r
48605         var node = this.getNode(e);\r
48606         if(this.disabled || !node || !node.ui){\r
48607             e.stopEvent();\r
48608             return false;\r
48609         }\r
48610         return true;\r
48611     },\r
48612 \r
48613     disable: function(){\r
48614         this.disabled = true;\r
48615     },\r
48616 \r
48617     enable: function(){\r
48618         this.disabled = false;\r
48619     }\r
48620 };/**\r
48621  * @class Ext.tree.DefaultSelectionModel\r
48622  * @extends Ext.util.Observable\r
48623  * The default single selection for a TreePanel.\r
48624  */\r
48625 Ext.tree.DefaultSelectionModel = function(config){\r
48626    this.selNode = null;\r
48627    \r
48628    this.addEvents(\r
48629        /**\r
48630         * @event selectionchange\r
48631         * Fires when the selected node changes\r
48632         * @param {DefaultSelectionModel} this\r
48633         * @param {TreeNode} node the new selection\r
48634         */\r
48635        'selectionchange',\r
48636 \r
48637        /**\r
48638         * @event beforeselect\r
48639         * Fires before the selected node changes, return false to cancel the change\r
48640         * @param {DefaultSelectionModel} this\r
48641         * @param {TreeNode} node the new selection\r
48642         * @param {TreeNode} node the old selection\r
48643         */\r
48644        'beforeselect'\r
48645    );\r
48646 \r
48647     Ext.apply(this, config);\r
48648     Ext.tree.DefaultSelectionModel.superclass.constructor.call(this);\r
48649 };\r
48650 \r
48651 Ext.extend(Ext.tree.DefaultSelectionModel, Ext.util.Observable, {\r
48652     init : function(tree){\r
48653         this.tree = tree;\r
48654         tree.mon(tree.getTreeEl(), 'keydown', this.onKeyDown, this);\r
48655         tree.on('click', this.onNodeClick, this);\r
48656     },\r
48657     \r
48658     onNodeClick : function(node, e){\r
48659         this.select(node);\r
48660     },\r
48661     \r
48662     /**\r
48663      * Select a node.\r
48664      * @param {TreeNode} node The node to select\r
48665      * @return {TreeNode} The selected node\r
48666      */\r
48667     select : function(node, /* private*/ selectNextNode){\r
48668         // If node is hidden, select the next node in whatever direction was being moved in.\r
48669         if (!Ext.fly(node.ui.wrap).isVisible() && selectNextNode) {\r
48670             return selectNextNode.call(this, node);\r
48671         }\r
48672         var last = this.selNode;\r
48673         if(node == last){\r
48674             node.ui.onSelectedChange(true);\r
48675         }else if(this.fireEvent('beforeselect', this, node, last) !== false){\r
48676             if(last && last.ui){\r
48677                 last.ui.onSelectedChange(false);\r
48678             }\r
48679             this.selNode = node;\r
48680             node.ui.onSelectedChange(true);\r
48681             this.fireEvent('selectionchange', this, node, last);\r
48682         }\r
48683         return node;\r
48684     },\r
48685     \r
48686     /**\r
48687      * Deselect a node.\r
48688      * @param {TreeNode} node The node to unselect\r
48689      * @param {Boolean} silent True to stop the selectionchange event from firing.\r
48690      */\r
48691     unselect : function(node, silent){\r
48692         if(this.selNode == node){\r
48693             this.clearSelections(silent);\r
48694         }    \r
48695     },\r
48696     \r
48697     /**\r
48698      * Clear all selections\r
48699      * @param {Boolean} silent True to stop the selectionchange event from firing.\r
48700      */\r
48701     clearSelections : function(silent){\r
48702         var n = this.selNode;\r
48703         if(n){\r
48704             n.ui.onSelectedChange(false);\r
48705             this.selNode = null;\r
48706             if(silent !== true){\r
48707                 this.fireEvent('selectionchange', this, null);\r
48708             }\r
48709         }\r
48710         return n;\r
48711     },\r
48712     \r
48713     /**\r
48714      * Get the selected node\r
48715      * @return {TreeNode} The selected node\r
48716      */\r
48717     getSelectedNode : function(){\r
48718         return this.selNode;    \r
48719     },\r
48720     \r
48721     /**\r
48722      * Returns true if the node is selected\r
48723      * @param {TreeNode} node The node to check\r
48724      * @return {Boolean}\r
48725      */\r
48726     isSelected : function(node){\r
48727         return this.selNode == node;  \r
48728     },\r
48729 \r
48730     /**\r
48731      * Selects the node above the selected node in the tree, intelligently walking the nodes\r
48732      * @return TreeNode The new selection\r
48733      */\r
48734     selectPrevious : function(/* private */ s){\r
48735         if(!(s = s || this.selNode || this.lastSelNode)){\r
48736             return null;\r
48737         }\r
48738         // Here we pass in the current function to select to indicate the direction we're moving\r
48739         var ps = s.previousSibling;\r
48740         if(ps){\r
48741             if(!ps.isExpanded() || ps.childNodes.length < 1){\r
48742                 return this.select(ps, this.selectPrevious);\r
48743             } else{\r
48744                 var lc = ps.lastChild;\r
48745                 while(lc && lc.isExpanded() && Ext.fly(lc.ui.wrap).isVisible() && lc.childNodes.length > 0){\r
48746                     lc = lc.lastChild;\r
48747                 }\r
48748                 return this.select(lc, this.selectPrevious);\r
48749             }\r
48750         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){\r
48751             return this.select(s.parentNode, this.selectPrevious);\r
48752         }\r
48753         return null;\r
48754     },\r
48755 \r
48756     /**\r
48757      * Selects the node above the selected node in the tree, intelligently walking the nodes\r
48758      * @return TreeNode The new selection\r
48759      */\r
48760     selectNext : function(/* private */ s){\r
48761         if(!(s = s || this.selNode || this.lastSelNode)){\r
48762             return null;\r
48763         }\r
48764         // Here we pass in the current function to select to indicate the direction we're moving\r
48765         if(s.firstChild && s.isExpanded() && Ext.fly(s.ui.wrap).isVisible()){\r
48766              return this.select(s.firstChild, this.selectNext);\r
48767          }else if(s.nextSibling){\r
48768              return this.select(s.nextSibling, this.selectNext);\r
48769          }else if(s.parentNode){\r
48770             var newS = null;\r
48771             s.parentNode.bubble(function(){\r
48772                 if(this.nextSibling){\r
48773                     newS = this.getOwnerTree().selModel.select(this.nextSibling, this.selectNext);\r
48774                     return false;\r
48775                 }\r
48776             });\r
48777             return newS;\r
48778          }\r
48779         return null;\r
48780     },\r
48781 \r
48782     onKeyDown : function(e){\r
48783         var s = this.selNode || this.lastSelNode;\r
48784         // undesirable, but required\r
48785         var sm = this;\r
48786         if(!s){\r
48787             return;\r
48788         }\r
48789         var k = e.getKey();\r
48790         switch(k){\r
48791              case e.DOWN:\r
48792                  e.stopEvent();\r
48793                  this.selectNext();\r
48794              break;\r
48795              case e.UP:\r
48796                  e.stopEvent();\r
48797                  this.selectPrevious();\r
48798              break;\r
48799              case e.RIGHT:\r
48800                  e.preventDefault();\r
48801                  if(s.hasChildNodes()){\r
48802                      if(!s.isExpanded()){\r
48803                          s.expand();\r
48804                      }else if(s.firstChild){\r
48805                          this.select(s.firstChild, e);\r
48806                      }\r
48807                  }\r
48808              break;\r
48809              case e.LEFT:\r
48810                  e.preventDefault();\r
48811                  if(s.hasChildNodes() && s.isExpanded()){\r
48812                      s.collapse();\r
48813                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){\r
48814                      this.select(s.parentNode, e);\r
48815                  }\r
48816              break;\r
48817         };\r
48818     }\r
48819 });\r
48820 \r
48821 /**\r
48822  * @class Ext.tree.MultiSelectionModel\r
48823  * @extends Ext.util.Observable\r
48824  * Multi selection for a TreePanel.\r
48825  */\r
48826 Ext.tree.MultiSelectionModel = function(config){\r
48827    this.selNodes = [];\r
48828    this.selMap = {};\r
48829    this.addEvents(\r
48830        /**\r
48831         * @event selectionchange\r
48832         * Fires when the selected nodes change\r
48833         * @param {MultiSelectionModel} this\r
48834         * @param {Array} nodes Array of the selected nodes\r
48835         */\r
48836        'selectionchange'\r
48837    );\r
48838     Ext.apply(this, config);\r
48839     Ext.tree.MultiSelectionModel.superclass.constructor.call(this);\r
48840 };\r
48841 \r
48842 Ext.extend(Ext.tree.MultiSelectionModel, Ext.util.Observable, {\r
48843     init : function(tree){\r
48844         this.tree = tree;\r
48845         tree.mon(tree.getTreeEl(), 'keydown', this.onKeyDown, this);\r
48846         tree.on('click', this.onNodeClick, this);\r
48847     },\r
48848     \r
48849     onNodeClick : function(node, e){\r
48850         if(e.ctrlKey && this.isSelected(node)){\r
48851             this.unselect(node);\r
48852         }else{\r
48853             this.select(node, e, e.ctrlKey);\r
48854         }\r
48855     },\r
48856     \r
48857     /**\r
48858      * Select a node.\r
48859      * @param {TreeNode} node The node to select\r
48860      * @param {EventObject} e (optional) An event associated with the selection\r
48861      * @param {Boolean} keepExisting True to retain existing selections\r
48862      * @return {TreeNode} The selected node\r
48863      */\r
48864     select : function(node, e, keepExisting){\r
48865         if(keepExisting !== true){\r
48866             this.clearSelections(true);\r
48867         }\r
48868         if(this.isSelected(node)){\r
48869             this.lastSelNode = node;\r
48870             return node;\r
48871         }\r
48872         this.selNodes.push(node);\r
48873         this.selMap[node.id] = node;\r
48874         this.lastSelNode = node;\r
48875         node.ui.onSelectedChange(true);\r
48876         this.fireEvent('selectionchange', this, this.selNodes);\r
48877         return node;\r
48878     },\r
48879     \r
48880     /**\r
48881      * Deselect a node.\r
48882      * @param {TreeNode} node The node to unselect\r
48883      */\r
48884     unselect : function(node){\r
48885         if(this.selMap[node.id]){\r
48886             node.ui.onSelectedChange(false);\r
48887             var sn = this.selNodes;\r
48888             var index = sn.indexOf(node);\r
48889             if(index != -1){\r
48890                 this.selNodes.splice(index, 1);\r
48891             }\r
48892             delete this.selMap[node.id];\r
48893             this.fireEvent('selectionchange', this, this.selNodes);\r
48894         }\r
48895     },\r
48896     \r
48897     /**\r
48898      * Clear all selections\r
48899      */\r
48900     clearSelections : function(suppressEvent){\r
48901         var sn = this.selNodes;\r
48902         if(sn.length > 0){\r
48903             for(var i = 0, len = sn.length; i < len; i++){\r
48904                 sn[i].ui.onSelectedChange(false);\r
48905             }\r
48906             this.selNodes = [];\r
48907             this.selMap = {};\r
48908             if(suppressEvent !== true){\r
48909                 this.fireEvent('selectionchange', this, this.selNodes);\r
48910             }\r
48911         }\r
48912     },\r
48913     \r
48914     /**\r
48915      * Returns true if the node is selected\r
48916      * @param {TreeNode} node The node to check\r
48917      * @return {Boolean}\r
48918      */\r
48919     isSelected : function(node){\r
48920         return this.selMap[node.id] ? true : false;  \r
48921     },\r
48922     \r
48923     /**\r
48924      * Returns an array of the selected nodes\r
48925      * @return {Array}\r
48926      */\r
48927     getSelectedNodes : function(){\r
48928         return this.selNodes;    \r
48929     },\r
48930 \r
48931     onKeyDown : Ext.tree.DefaultSelectionModel.prototype.onKeyDown,\r
48932 \r
48933     selectNext : Ext.tree.DefaultSelectionModel.prototype.selectNext,\r
48934 \r
48935     selectPrevious : Ext.tree.DefaultSelectionModel.prototype.selectPrevious\r
48936 });/**\r
48937  * @class Ext.data.Tree\r
48938  * @extends Ext.util.Observable\r
48939  * Represents a tree data structure and bubbles all the events for its nodes. The nodes\r
48940  * in the tree have most standard DOM functionality.\r
48941  * @constructor\r
48942  * @param {Node} root (optional) The root node\r
48943  */\r
48944 Ext.data.Tree = function(root){\r
48945    this.nodeHash = {};\r
48946    /**\r
48947     * The root node for this tree\r
48948     * @type Node\r
48949     */\r
48950    this.root = null;\r
48951    if(root){\r
48952        this.setRootNode(root);\r
48953    }\r
48954    this.addEvents(\r
48955        /**\r
48956         * @event append\r
48957         * Fires when a new child node is appended to a node in this tree.\r
48958         * @param {Tree} tree The owner tree\r
48959         * @param {Node} parent The parent node\r
48960         * @param {Node} node The newly appended node\r
48961         * @param {Number} index The index of the newly appended node\r
48962         */\r
48963        "append",\r
48964        /**\r
48965         * @event remove\r
48966         * Fires when a child node is removed from a node in this tree.\r
48967         * @param {Tree} tree The owner tree\r
48968         * @param {Node} parent The parent node\r
48969         * @param {Node} node The child node removed\r
48970         */\r
48971        "remove",\r
48972        /**\r
48973         * @event move\r
48974         * Fires when a node is moved to a new location in the tree\r
48975         * @param {Tree} tree The owner tree\r
48976         * @param {Node} node The node moved\r
48977         * @param {Node} oldParent The old parent of this node\r
48978         * @param {Node} newParent The new parent of this node\r
48979         * @param {Number} index The index it was moved to\r
48980         */\r
48981        "move",\r
48982        /**\r
48983         * @event insert\r
48984         * Fires when a new child node is inserted in a node in this tree.\r
48985         * @param {Tree} tree The owner tree\r
48986         * @param {Node} parent The parent node\r
48987         * @param {Node} node The child node inserted\r
48988         * @param {Node} refNode The child node the node was inserted before\r
48989         */\r
48990        "insert",\r
48991        /**\r
48992         * @event beforeappend\r
48993         * Fires before a new child is appended to a node in this tree, return false to cancel the append.\r
48994         * @param {Tree} tree The owner tree\r
48995         * @param {Node} parent The parent node\r
48996         * @param {Node} node The child node to be appended\r
48997         */\r
48998        "beforeappend",\r
48999        /**\r
49000         * @event beforeremove\r
49001         * Fires before a child is removed from a node in this tree, return false to cancel the remove.\r
49002         * @param {Tree} tree The owner tree\r
49003         * @param {Node} parent The parent node\r
49004         * @param {Node} node The child node to be removed\r
49005         */\r
49006        "beforeremove",\r
49007        /**\r
49008         * @event beforemove\r
49009         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.\r
49010         * @param {Tree} tree The owner tree\r
49011         * @param {Node} node The node being moved\r
49012         * @param {Node} oldParent The parent of the node\r
49013         * @param {Node} newParent The new parent the node is moving to\r
49014         * @param {Number} index The index it is being moved to\r
49015         */\r
49016        "beforemove",\r
49017        /**\r
49018         * @event beforeinsert\r
49019         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.\r
49020         * @param {Tree} tree The owner tree\r
49021         * @param {Node} parent The parent node\r
49022         * @param {Node} node The child node to be inserted\r
49023         * @param {Node} refNode The child node the node is being inserted before\r
49024         */\r
49025        "beforeinsert"\r
49026    );\r
49027 \r
49028     Ext.data.Tree.superclass.constructor.call(this);\r
49029 };\r
49030 \r
49031 Ext.extend(Ext.data.Tree, Ext.util.Observable, {\r
49032     /**\r
49033      * @cfg {String} pathSeparator\r
49034      * The token used to separate paths in node ids (defaults to '/').\r
49035      */\r
49036     pathSeparator: "/",\r
49037 \r
49038     // private\r
49039     proxyNodeEvent : function(){\r
49040         return this.fireEvent.apply(this, arguments);\r
49041     },\r
49042 \r
49043     /**\r
49044      * Returns the root node for this tree.\r
49045      * @return {Node}\r
49046      */\r
49047     getRootNode : function(){\r
49048         return this.root;\r
49049     },\r
49050 \r
49051     /**\r
49052      * Sets the root node for this tree.\r
49053      * @param {Node} node\r
49054      * @return {Node}\r
49055      */\r
49056     setRootNode : function(node){\r
49057         this.root = node;\r
49058         node.ownerTree = this;\r
49059         node.isRoot = true;\r
49060         this.registerNode(node);\r
49061         return node;\r
49062     },\r
49063 \r
49064     /**\r
49065      * Gets a node in this tree by its id.\r
49066      * @param {String} id\r
49067      * @return {Node}\r
49068      */\r
49069     getNodeById : function(id){\r
49070         return this.nodeHash[id];\r
49071     },\r
49072 \r
49073     // private\r
49074     registerNode : function(node){\r
49075         this.nodeHash[node.id] = node;\r
49076     },\r
49077 \r
49078     // private\r
49079     unregisterNode : function(node){\r
49080         delete this.nodeHash[node.id];\r
49081     },\r
49082 \r
49083     toString : function(){\r
49084         return "[Tree"+(this.id?" "+this.id:"")+"]";\r
49085     }\r
49086 });\r
49087 \r
49088 /**\r
49089  * @class Ext.data.Node\r
49090  * @extends Ext.util.Observable\r
49091  * @cfg {Boolean} leaf true if this node is a leaf and does not have children\r
49092  * @cfg {String} id The id for this node. If one is not specified, one is generated.\r
49093  * @constructor\r
49094  * @param {Object} attributes The attributes/config for the node\r
49095  */\r
49096 Ext.data.Node = function(attributes){\r
49097     /**\r
49098      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.\r
49099      * @type {Object}\r
49100      */\r
49101     this.attributes = attributes || {};\r
49102     this.leaf = this.attributes.leaf;\r
49103     /**\r
49104      * The node id. @type String\r
49105      */\r
49106     this.id = this.attributes.id;\r
49107     if(!this.id){\r
49108         this.id = Ext.id(null, "xnode-");\r
49109         this.attributes.id = this.id;\r
49110     }\r
49111     /**\r
49112      * All child nodes of this node. @type Array\r
49113      */\r
49114     this.childNodes = [];\r
49115     if(!this.childNodes.indexOf){ // indexOf is a must\r
49116         this.childNodes.indexOf = function(o){\r
49117             for(var i = 0, len = this.length; i < len; i++){\r
49118                 if(this[i] == o){\r
49119                     return i;\r
49120                 }\r
49121             }\r
49122             return -1;\r
49123         };\r
49124     }\r
49125     /**\r
49126      * The parent node for this node. @type Node\r
49127      */\r
49128     this.parentNode = null;\r
49129     /**\r
49130      * The first direct child node of this node, or null if this node has no child nodes. @type Node\r
49131      */\r
49132     this.firstChild = null;\r
49133     /**\r
49134      * The last direct child node of this node, or null if this node has no child nodes. @type Node\r
49135      */\r
49136     this.lastChild = null;\r
49137     /**\r
49138      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node\r
49139      */\r
49140     this.previousSibling = null;\r
49141     /**\r
49142      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node\r
49143      */\r
49144     this.nextSibling = null;\r
49145 \r
49146     this.addEvents({\r
49147        /**\r
49148         * @event append\r
49149         * Fires when a new child node is appended\r
49150         * @param {Tree} tree The owner tree\r
49151         * @param {Node} this This node\r
49152         * @param {Node} node The newly appended node\r
49153         * @param {Number} index The index of the newly appended node\r
49154         */\r
49155        "append" : true,\r
49156        /**\r
49157         * @event remove\r
49158         * Fires when a child node is removed\r
49159         * @param {Tree} tree The owner tree\r
49160         * @param {Node} this This node\r
49161         * @param {Node} node The removed node\r
49162         */\r
49163        "remove" : true,\r
49164        /**\r
49165         * @event move\r
49166         * Fires when this node is moved to a new location in the tree\r
49167         * @param {Tree} tree The owner tree\r
49168         * @param {Node} this This node\r
49169         * @param {Node} oldParent The old parent of this node\r
49170         * @param {Node} newParent The new parent of this node\r
49171         * @param {Number} index The index it was moved to\r
49172         */\r
49173        "move" : true,\r
49174        /**\r
49175         * @event insert\r
49176         * Fires when a new child node is inserted.\r
49177         * @param {Tree} tree The owner tree\r
49178         * @param {Node} this This node\r
49179         * @param {Node} node The child node inserted\r
49180         * @param {Node} refNode The child node the node was inserted before\r
49181         */\r
49182        "insert" : true,\r
49183        /**\r
49184         * @event beforeappend\r
49185         * Fires before a new child is appended, return false to cancel the append.\r
49186         * @param {Tree} tree The owner tree\r
49187         * @param {Node} this This node\r
49188         * @param {Node} node The child node to be appended\r
49189         */\r
49190        "beforeappend" : true,\r
49191        /**\r
49192         * @event beforeremove\r
49193         * Fires before a child is removed, return false to cancel the remove.\r
49194         * @param {Tree} tree The owner tree\r
49195         * @param {Node} this This node\r
49196         * @param {Node} node The child node to be removed\r
49197         */\r
49198        "beforeremove" : true,\r
49199        /**\r
49200         * @event beforemove\r
49201         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.\r
49202         * @param {Tree} tree The owner tree\r
49203         * @param {Node} this This node\r
49204         * @param {Node} oldParent The parent of this node\r
49205         * @param {Node} newParent The new parent this node is moving to\r
49206         * @param {Number} index The index it is being moved to\r
49207         */\r
49208        "beforemove" : true,\r
49209        /**\r
49210         * @event beforeinsert\r
49211         * Fires before a new child is inserted, return false to cancel the insert.\r
49212         * @param {Tree} tree The owner tree\r
49213         * @param {Node} this This node\r
49214         * @param {Node} node The child node to be inserted\r
49215         * @param {Node} refNode The child node the node is being inserted before\r
49216         */\r
49217        "beforeinsert" : true\r
49218    });\r
49219     this.listeners = this.attributes.listeners;\r
49220     Ext.data.Node.superclass.constructor.call(this);\r
49221 };\r
49222 \r
49223 Ext.extend(Ext.data.Node, Ext.util.Observable, {\r
49224     // private\r
49225     fireEvent : function(evtName){\r
49226         // first do standard event for this node\r
49227         if(Ext.data.Node.superclass.fireEvent.apply(this, arguments) === false){\r
49228             return false;\r
49229         }\r
49230         // then bubble it up to the tree if the event wasn't cancelled\r
49231         var ot = this.getOwnerTree();\r
49232         if(ot){\r
49233             if(ot.proxyNodeEvent.apply(ot, arguments) === false){\r
49234                 return false;\r
49235             }\r
49236         }\r
49237         return true;\r
49238     },\r
49239 \r
49240     /**\r
49241      * Returns true if this node is a leaf\r
49242      * @return {Boolean}\r
49243      */\r
49244     isLeaf : function(){\r
49245         return this.leaf === true;\r
49246     },\r
49247 \r
49248     // private\r
49249     setFirstChild : function(node){\r
49250         this.firstChild = node;\r
49251     },\r
49252 \r
49253     //private\r
49254     setLastChild : function(node){\r
49255         this.lastChild = node;\r
49256     },\r
49257 \r
49258 \r
49259     /**\r
49260      * Returns true if this node is the last child of its parent\r
49261      * @return {Boolean}\r
49262      */\r
49263     isLast : function(){\r
49264        return (!this.parentNode ? true : this.parentNode.lastChild == this);\r
49265     },\r
49266 \r
49267     /**\r
49268      * Returns true if this node is the first child of its parent\r
49269      * @return {Boolean}\r
49270      */\r
49271     isFirst : function(){\r
49272        return (!this.parentNode ? true : this.parentNode.firstChild == this);\r
49273     },\r
49274 \r
49275     /**\r
49276      * Returns true if this node has one or more child nodes, else false.\r
49277      * @return {Boolean}\r
49278      */\r
49279     hasChildNodes : function(){\r
49280         return !this.isLeaf() && this.childNodes.length > 0;\r
49281     },\r
49282     \r
49283     /**\r
49284      * Returns true if this node has one or more child nodes, or if the <tt>expandable</tt>\r
49285      * node attribute is explicitly specified as true (see {@link #attributes}), otherwise returns false.\r
49286      * @return {Boolean}\r
49287      */\r
49288     isExpandable : function(){\r
49289         return this.attributes.expandable || this.hasChildNodes();\r
49290     },\r
49291 \r
49292     /**\r
49293      * Insert node(s) as the last child node of this node.\r
49294      * @param {Node/Array} node The node or Array of nodes to append\r
49295      * @return {Node} The appended node if single append, or null if an array was passed\r
49296      */\r
49297     appendChild : function(node){\r
49298         var multi = false;\r
49299         if(Ext.isArray(node)){\r
49300             multi = node;\r
49301         }else if(arguments.length > 1){\r
49302             multi = arguments;\r
49303         }\r
49304         // if passed an array or multiple args do them one by one\r
49305         if(multi){\r
49306             for(var i = 0, len = multi.length; i < len; i++) {\r
49307                 this.appendChild(multi[i]);\r
49308             }\r
49309         }else{\r
49310             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){\r
49311                 return false;\r
49312             }\r
49313             var index = this.childNodes.length;\r
49314             var oldParent = node.parentNode;\r
49315             // it's a move, make sure we move it cleanly\r
49316             if(oldParent){\r
49317                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){\r
49318                     return false;\r
49319                 }\r
49320                 oldParent.removeChild(node);\r
49321             }\r
49322             index = this.childNodes.length;\r
49323             if(index === 0){\r
49324                 this.setFirstChild(node);\r
49325             }\r
49326             this.childNodes.push(node);\r
49327             node.parentNode = this;\r
49328             var ps = this.childNodes[index-1];\r
49329             if(ps){\r
49330                 node.previousSibling = ps;\r
49331                 ps.nextSibling = node;\r
49332             }else{\r
49333                 node.previousSibling = null;\r
49334             }\r
49335             node.nextSibling = null;\r
49336             this.setLastChild(node);\r
49337             node.setOwnerTree(this.getOwnerTree());\r
49338             this.fireEvent("append", this.ownerTree, this, node, index);\r
49339             if(oldParent){\r
49340                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);\r
49341             }\r
49342             return node;\r
49343         }\r
49344     },\r
49345 \r
49346     /**\r
49347      * Removes a child node from this node.\r
49348      * @param {Node} node The node to remove\r
49349      * @param {Boolean} destroy <tt>true</tt> to destroy the node upon removal. Defaults to <tt>false</tt>.\r
49350      * @return {Node} The removed node\r
49351      */\r
49352     removeChild : function(node, destroy){\r
49353         var index = this.childNodes.indexOf(node);\r
49354         if(index == -1){\r
49355             return false;\r
49356         }\r
49357         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){\r
49358             return false;\r
49359         }\r
49360 \r
49361         // remove it from childNodes collection\r
49362         this.childNodes.splice(index, 1);\r
49363 \r
49364         // update siblings\r
49365         if(node.previousSibling){\r
49366             node.previousSibling.nextSibling = node.nextSibling;\r
49367         }\r
49368         if(node.nextSibling){\r
49369             node.nextSibling.previousSibling = node.previousSibling;\r
49370         }\r
49371 \r
49372         // update child refs\r
49373         if(this.firstChild == node){\r
49374             this.setFirstChild(node.nextSibling);\r
49375         }\r
49376         if(this.lastChild == node){\r
49377             this.setLastChild(node.previousSibling);\r
49378         }\r
49379 \r
49380         node.clear();\r
49381         this.fireEvent("remove", this.ownerTree, this, node);\r
49382         if(destroy){\r
49383             node.destroy();\r
49384         }\r
49385         return node;\r
49386     },\r
49387     \r
49388     // private\r
49389     clear : function(destroy){\r
49390         // clear any references from the node\r
49391         this.setOwnerTree(null, destroy);\r
49392         this.parentNode = this.previousSibling = this.nextSibling = null\r
49393         if(destroy){\r
49394             this.firstChild = this.lastChild = null; \r
49395         }\r
49396     },\r
49397     \r
49398     /**\r
49399      * Destroys the node.\r
49400      */\r
49401     destroy : function(){\r
49402         this.purgeListeners();\r
49403         this.clear(true);  \r
49404         Ext.each(this.childNodes, function(n){\r
49405             n.destroy();\r
49406         });\r
49407         this.childNodes = null;\r
49408     },\r
49409 \r
49410     /**\r
49411      * Inserts the first node before the second node in this nodes childNodes collection.\r
49412      * @param {Node} node The node to insert\r
49413      * @param {Node} refNode The node to insert before (if null the node is appended)\r
49414      * @return {Node} The inserted node\r
49415      */\r
49416     insertBefore : function(node, refNode){\r
49417         if(!refNode){ // like standard Dom, refNode can be null for append\r
49418             return this.appendChild(node);\r
49419         }\r
49420         // nothing to do\r
49421         if(node == refNode){\r
49422             return false;\r
49423         }\r
49424 \r
49425         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){\r
49426             return false;\r
49427         }\r
49428         var index = this.childNodes.indexOf(refNode);\r
49429         var oldParent = node.parentNode;\r
49430         var refIndex = index;\r
49431 \r
49432         // when moving internally, indexes will change after remove\r
49433         if(oldParent == this && this.childNodes.indexOf(node) < index){\r
49434             refIndex--;\r
49435         }\r
49436 \r
49437         // it's a move, make sure we move it cleanly\r
49438         if(oldParent){\r
49439             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){\r
49440                 return false;\r
49441             }\r
49442             oldParent.removeChild(node);\r
49443         }\r
49444         if(refIndex === 0){\r
49445             this.setFirstChild(node);\r
49446         }\r
49447         this.childNodes.splice(refIndex, 0, node);\r
49448         node.parentNode = this;\r
49449         var ps = this.childNodes[refIndex-1];\r
49450         if(ps){\r
49451             node.previousSibling = ps;\r
49452             ps.nextSibling = node;\r
49453         }else{\r
49454             node.previousSibling = null;\r
49455         }\r
49456         node.nextSibling = refNode;\r
49457         refNode.previousSibling = node;\r
49458         node.setOwnerTree(this.getOwnerTree());\r
49459         this.fireEvent("insert", this.ownerTree, this, node, refNode);\r
49460         if(oldParent){\r
49461             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);\r
49462         }\r
49463         return node;\r
49464     },\r
49465 \r
49466     /**\r
49467      * Removes this node from its parent\r
49468      * @param {Boolean} destroy <tt>true</tt> to destroy the node upon removal. Defaults to <tt>false</tt>.\r
49469      * @return {Node} this\r
49470      */\r
49471     remove : function(destroy){\r
49472         this.parentNode.removeChild(this, destroy);\r
49473         return this;\r
49474     },\r
49475     \r
49476     /**\r
49477      * Removes all child nodes from this node.\r
49478      * @param {Boolean} destroy <tt>true</tt> to destroy the node upon removal. Defaults to <tt>false</tt>.\r
49479      * @return {Node} this\r
49480      */\r
49481     removeAll : function(destroy){\r
49482         var cn = this.childNodes,\r
49483             n;\r
49484         while((n = cn[0])){\r
49485             this.removeChild(n, destroy);\r
49486         }\r
49487         return this;\r
49488     },\r
49489 \r
49490     /**\r
49491      * Returns the child node at the specified index.\r
49492      * @param {Number} index\r
49493      * @return {Node}\r
49494      */\r
49495     item : function(index){\r
49496         return this.childNodes[index];\r
49497     },\r
49498 \r
49499     /**\r
49500      * Replaces one child node in this node with another.\r
49501      * @param {Node} newChild The replacement node\r
49502      * @param {Node} oldChild The node to replace\r
49503      * @return {Node} The replaced node\r
49504      */\r
49505     replaceChild : function(newChild, oldChild){\r
49506         var s = oldChild ? oldChild.nextSibling : null;\r
49507         this.removeChild(oldChild);\r
49508         this.insertBefore(newChild, s);\r
49509         return oldChild;\r
49510     },\r
49511 \r
49512     /**\r
49513      * Returns the index of a child node\r
49514      * @param {Node} node\r
49515      * @return {Number} The index of the node or -1 if it was not found\r
49516      */\r
49517     indexOf : function(child){\r
49518         return this.childNodes.indexOf(child);\r
49519     },\r
49520 \r
49521     /**\r
49522      * Returns the tree this node is in.\r
49523      * @return {Tree}\r
49524      */\r
49525     getOwnerTree : function(){\r
49526         // if it doesn't have one, look for one\r
49527         if(!this.ownerTree){\r
49528             var p = this;\r
49529             while(p){\r
49530                 if(p.ownerTree){\r
49531                     this.ownerTree = p.ownerTree;\r
49532                     break;\r
49533                 }\r
49534                 p = p.parentNode;\r
49535             }\r
49536         }\r
49537         return this.ownerTree;\r
49538     },\r
49539 \r
49540     /**\r
49541      * Returns depth of this node (the root node has a depth of 0)\r
49542      * @return {Number}\r
49543      */\r
49544     getDepth : function(){\r
49545         var depth = 0;\r
49546         var p = this;\r
49547         while(p.parentNode){\r
49548             ++depth;\r
49549             p = p.parentNode;\r
49550         }\r
49551         return depth;\r
49552     },\r
49553 \r
49554     // private\r
49555     setOwnerTree : function(tree, destroy){\r
49556         // if it is a move, we need to update everyone\r
49557         if(tree != this.ownerTree){\r
49558             if(this.ownerTree){\r
49559                 this.ownerTree.unregisterNode(this);\r
49560             }\r
49561             this.ownerTree = tree;\r
49562             // If we're destroying, we don't need to recurse since it will be called on each child node\r
49563             if(destroy !== true){\r
49564                 Ext.each(this.childNodes, function(n){\r
49565                     n.setOwnerTree(tree);\r
49566                 });\r
49567             }\r
49568             if(tree){\r
49569                 tree.registerNode(this);\r
49570             }\r
49571         }\r
49572     },\r
49573     \r
49574     /**\r
49575      * Changes the id of this node.\r
49576      * @param {String} id The new id for the node.\r
49577      */\r
49578     setId: function(id){\r
49579         if(id !== this.id){\r
49580             var t = this.ownerTree;\r
49581             if(t){\r
49582                 t.unregisterNode(this);\r
49583             }\r
49584             this.id = this.attributes.id = id;\r
49585             if(t){\r
49586                 t.registerNode(this);\r
49587             }\r
49588             this.onIdChange(id);\r
49589         }\r
49590     },\r
49591     \r
49592     // private\r
49593     onIdChange: Ext.emptyFn,\r
49594 \r
49595     /**\r
49596      * Returns the path for this node. The path can be used to expand or select this node programmatically.\r
49597      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)\r
49598      * @return {String} The path\r
49599      */\r
49600     getPath : function(attr){\r
49601         attr = attr || "id";\r
49602         var p = this.parentNode;\r
49603         var b = [this.attributes[attr]];\r
49604         while(p){\r
49605             b.unshift(p.attributes[attr]);\r
49606             p = p.parentNode;\r
49607         }\r
49608         var sep = this.getOwnerTree().pathSeparator;\r
49609         return sep + b.join(sep);\r
49610     },\r
49611 \r
49612     /**\r
49613      * Bubbles up the tree from this node, calling the specified function with each node. The arguments to the function\r
49614      * will be the args provided or the current node. If the function returns false at any point,\r
49615      * the bubble is stopped.\r
49616      * @param {Function} fn The function to call\r
49617      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the current Node.\r
49618      * @param {Array} args (optional) The args to call the function with (default to passing the current Node)\r
49619      */\r
49620     bubble : function(fn, scope, args){\r
49621         var p = this;\r
49622         while(p){\r
49623             if(fn.apply(scope || p, args || [p]) === false){\r
49624                 break;\r
49625             }\r
49626             p = p.parentNode;\r
49627         }\r
49628     },\r
49629 \r
49630     /**\r
49631      * Cascades down the tree from this node, calling the specified function with each node. The arguments to the function\r
49632      * will be the args provided or the current node. If the function returns false at any point,\r
49633      * the cascade is stopped on that branch.\r
49634      * @param {Function} fn The function to call\r
49635      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the current Node.\r
49636      * @param {Array} args (optional) The args to call the function with (default to passing the current Node)\r
49637      */\r
49638     cascade : function(fn, scope, args){\r
49639         if(fn.apply(scope || this, args || [this]) !== false){\r
49640             var cs = this.childNodes;\r
49641             for(var i = 0, len = cs.length; i < len; i++) {\r
49642                 cs[i].cascade(fn, scope, args);\r
49643             }\r
49644         }\r
49645     },\r
49646 \r
49647     /**\r
49648      * Interates the child nodes of this node, calling the specified function with each node. The arguments to the function\r
49649      * will be the args provided or the current node. If the function returns false at any point,\r
49650      * the iteration stops.\r
49651      * @param {Function} fn The function to call\r
49652      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the current Node in the iteration.\r
49653      * @param {Array} args (optional) The args to call the function with (default to passing the current Node)\r
49654      */\r
49655     eachChild : function(fn, scope, args){\r
49656         var cs = this.childNodes;\r
49657         for(var i = 0, len = cs.length; i < len; i++) {\r
49658                 if(fn.apply(scope || this, args || [cs[i]]) === false){\r
49659                     break;\r
49660                 }\r
49661         }\r
49662     },\r
49663 \r
49664     /**\r
49665      * Finds the first child that has the attribute with the specified value.\r
49666      * @param {String} attribute The attribute name\r
49667      * @param {Mixed} value The value to search for\r
49668      * @return {Node} The found child or null if none was found\r
49669      */\r
49670     findChild : function(attribute, value){\r
49671         var cs = this.childNodes;\r
49672         for(var i = 0, len = cs.length; i < len; i++) {\r
49673                 if(cs[i].attributes[attribute] == value){\r
49674                     return cs[i];\r
49675                 }\r
49676         }\r
49677         return null;\r
49678     },\r
49679 \r
49680     /**\r
49681      * Finds the first child by a custom function. The child matches if the function passed returns <code>true</code>.\r
49682      * @param {Function} fn A function which must return <code>true</code> if the passed Node is the required Node.\r
49683      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the Node being tested.\r
49684      * @return {Node} The found child or null if none was found\r
49685      */\r
49686     findChildBy : function(fn, scope){\r
49687         var cs = this.childNodes;\r
49688         for(var i = 0, len = cs.length; i < len; i++) {\r
49689                 if(fn.call(scope||cs[i], cs[i]) === true){\r
49690                     return cs[i];\r
49691                 }\r
49692         }\r
49693         return null;\r
49694     },\r
49695 \r
49696     /**\r
49697      * Sorts this nodes children using the supplied sort function.\r
49698      * @param {Function} fn A function which, when passed two Nodes, returns -1, 0 or 1 depending upon required sort order.\r
49699      * @param {Object} scope (optional)The scope (<code>this</code> reference) in which the function is executed. Defaults to the browser window.\r
49700      */\r
49701     sort : function(fn, scope){\r
49702         var cs = this.childNodes;\r
49703         var len = cs.length;\r
49704         if(len > 0){\r
49705             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;\r
49706             cs.sort(sortFn);\r
49707             for(var i = 0; i < len; i++){\r
49708                 var n = cs[i];\r
49709                 n.previousSibling = cs[i-1];\r
49710                 n.nextSibling = cs[i+1];\r
49711                 if(i === 0){\r
49712                     this.setFirstChild(n);\r
49713                 }\r
49714                 if(i == len-1){\r
49715                     this.setLastChild(n);\r
49716                 }\r
49717             }\r
49718         }\r
49719     },\r
49720 \r
49721     /**\r
49722      * Returns true if this node is an ancestor (at any point) of the passed node.\r
49723      * @param {Node} node\r
49724      * @return {Boolean}\r
49725      */\r
49726     contains : function(node){\r
49727         return node.isAncestor(this);\r
49728     },\r
49729 \r
49730     /**\r
49731      * Returns true if the passed node is an ancestor (at any point) of this node.\r
49732      * @param {Node} node\r
49733      * @return {Boolean}\r
49734      */\r
49735     isAncestor : function(node){\r
49736         var p = this.parentNode;\r
49737         while(p){\r
49738             if(p == node){\r
49739                 return true;\r
49740             }\r
49741             p = p.parentNode;\r
49742         }\r
49743         return false;\r
49744     },\r
49745 \r
49746     toString : function(){\r
49747         return "[Node"+(this.id?" "+this.id:"")+"]";\r
49748     }\r
49749 });/**
49750  * @class Ext.tree.TreeNode
49751  * @extends Ext.data.Node
49752  * @cfg {String} text The text for this node
49753  * @cfg {Boolean} expanded true to start the node expanded
49754  * @cfg {Boolean} allowDrag False to make this node undraggable if {@link #draggable} = true (defaults to true)
49755  * @cfg {Boolean} allowDrop False if this node cannot have child nodes dropped on it (defaults to true)
49756  * @cfg {Boolean} disabled true to start the node disabled
49757  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
49758  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
49759  * @cfg {String} cls A css class to be added to the node
49760  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
49761  * @cfg {String} href URL of the link used for the node (defaults to #)
49762  * @cfg {String} hrefTarget target frame for the link
49763  * @cfg {Boolean} hidden True to render hidden. (Defaults to false).
49764  * @cfg {String} qtip An Ext QuickTip for the node
49765  * @cfg {Boolean} expandable If set to true, the node will always show a plus/minus icon, even when empty
49766  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
49767  * @cfg {Boolean} singleClickExpand True for single click expand on this node
49768  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Ext.tree.TreeNodeUI)
49769  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
49770  * (defaults to undefined with no checkbox rendered)
49771  * @cfg {Boolean} draggable True to make this node draggable (defaults to false)
49772  * @cfg {Boolean} isTarget False to not allow this node to act as a drop target (defaults to true)
49773  * @cfg {Boolean} allowChildren False to not allow this node to have child nodes (defaults to true)
49774  * @cfg {Boolean} editable False to not allow this node to be edited by an {@link Ext.tree.TreeEditor} (defaults to true)
49775  * @constructor
49776  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
49777  */
49778 Ext.tree.TreeNode = function(attributes){
49779     attributes = attributes || {};
49780     if(Ext.isString(attributes)){
49781         attributes = {text: attributes};
49782     }
49783     this.childrenRendered = false;
49784     this.rendered = false;
49785     Ext.tree.TreeNode.superclass.constructor.call(this, attributes);
49786     this.expanded = attributes.expanded === true;
49787     this.isTarget = attributes.isTarget !== false;
49788     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
49789     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
49790
49791     /**
49792      * Read-only. The text for this node. To change it use <code>{@link #setText}</code>.
49793      * @type String
49794      */
49795     this.text = attributes.text;
49796     /**
49797      * True if this node is disabled.
49798      * @type Boolean
49799      */
49800     this.disabled = attributes.disabled === true;
49801     /**
49802      * True if this node is hidden.
49803      * @type Boolean
49804      */
49805     this.hidden = attributes.hidden === true;
49806
49807     this.addEvents(
49808         /**
49809         * @event textchange
49810         * Fires when the text for this node is changed
49811         * @param {Node} this This node
49812         * @param {String} text The new text
49813         * @param {String} oldText The old text
49814         */
49815         'textchange',
49816         /**
49817         * @event beforeexpand
49818         * Fires before this node is expanded, return false to cancel.
49819         * @param {Node} this This node
49820         * @param {Boolean} deep
49821         * @param {Boolean} anim
49822         */
49823         'beforeexpand',
49824         /**
49825         * @event beforecollapse
49826         * Fires before this node is collapsed, return false to cancel.
49827         * @param {Node} this This node
49828         * @param {Boolean} deep
49829         * @param {Boolean} anim
49830         */
49831         'beforecollapse',
49832         /**
49833         * @event expand
49834         * Fires when this node is expanded
49835         * @param {Node} this This node
49836         */
49837         'expand',
49838         /**
49839         * @event disabledchange
49840         * Fires when the disabled status of this node changes
49841         * @param {Node} this This node
49842         * @param {Boolean} disabled
49843         */
49844         'disabledchange',
49845         /**
49846         * @event collapse
49847         * Fires when this node is collapsed
49848         * @param {Node} this This node
49849         */
49850         'collapse',
49851         /**
49852         * @event beforeclick
49853         * Fires before click processing. Return false to cancel the default action.
49854         * @param {Node} this This node
49855         * @param {Ext.EventObject} e The event object
49856         */
49857         'beforeclick',
49858         /**
49859         * @event click
49860         * Fires when this node is clicked
49861         * @param {Node} this This node
49862         * @param {Ext.EventObject} e The event object
49863         */
49864         'click',
49865         /**
49866         * @event checkchange
49867         * Fires when a node with a checkbox's checked property changes
49868         * @param {Node} this This node
49869         * @param {Boolean} checked
49870         */
49871         'checkchange',
49872         /**
49873         * @event beforedblclick
49874         * Fires before double click processing. Return false to cancel the default action.
49875         * @param {Node} this This node
49876         * @param {Ext.EventObject} e The event object
49877         */
49878         'beforedblclick',
49879         /**
49880         * @event dblclick
49881         * Fires when this node is double clicked
49882         * @param {Node} this This node
49883         * @param {Ext.EventObject} e The event object
49884         */
49885         'dblclick',
49886         /**
49887         * @event contextmenu
49888         * Fires when this node is right clicked
49889         * @param {Node} this This node
49890         * @param {Ext.EventObject} e The event object
49891         */
49892         'contextmenu',
49893         /**
49894         * @event beforechildrenrendered
49895         * Fires right before the child nodes for this node are rendered
49896         * @param {Node} this This node
49897         */
49898         'beforechildrenrendered'
49899     );
49900
49901     var uiClass = this.attributes.uiProvider || this.defaultUI || Ext.tree.TreeNodeUI;
49902
49903     /**
49904      * Read-only. The UI for this node
49905      * @type TreeNodeUI
49906      */
49907     this.ui = new uiClass(this);
49908 };
49909 Ext.extend(Ext.tree.TreeNode, Ext.data.Node, {
49910     preventHScroll : true,
49911     /**
49912      * Returns true if this node is expanded
49913      * @return {Boolean}
49914      */
49915     isExpanded : function(){
49916         return this.expanded;
49917     },
49918
49919 /**
49920  * Returns the UI object for this node.
49921  * @return {TreeNodeUI} The object which is providing the user interface for this tree
49922  * node. Unless otherwise specified in the {@link #uiProvider}, this will be an instance
49923  * of {@link Ext.tree.TreeNodeUI}
49924  */
49925     getUI : function(){
49926         return this.ui;
49927     },
49928
49929     getLoader : function(){
49930         var owner;
49931         return this.loader || ((owner = this.getOwnerTree()) && owner.loader ? owner.loader : (this.loader = new Ext.tree.TreeLoader()));
49932     },
49933
49934     // private override
49935     setFirstChild : function(node){
49936         var of = this.firstChild;
49937         Ext.tree.TreeNode.superclass.setFirstChild.call(this, node);
49938         if(this.childrenRendered && of && node != of){
49939             of.renderIndent(true, true);
49940         }
49941         if(this.rendered){
49942             this.renderIndent(true, true);
49943         }
49944     },
49945
49946     // private override
49947     setLastChild : function(node){
49948         var ol = this.lastChild;
49949         Ext.tree.TreeNode.superclass.setLastChild.call(this, node);
49950         if(this.childrenRendered && ol && node != ol){
49951             ol.renderIndent(true, true);
49952         }
49953         if(this.rendered){
49954             this.renderIndent(true, true);
49955         }
49956     },
49957
49958     // these methods are overridden to provide lazy rendering support
49959     // private override
49960     appendChild : function(n){
49961         var node, exists;
49962         if(!n.render && !Ext.isArray(n)){
49963             n = this.getLoader().createNode(n);
49964         }else{
49965             exists = !n.parentNode;
49966         }
49967         node = Ext.tree.TreeNode.superclass.appendChild.call(this, n);
49968         if(node){
49969             this.afterAdd(node, exists);
49970         }
49971         this.ui.updateExpandIcon();
49972         return node;
49973     },
49974
49975     // private override
49976     removeChild : function(node, destroy){
49977         this.ownerTree.getSelectionModel().unselect(node);
49978         Ext.tree.TreeNode.superclass.removeChild.apply(this, arguments);
49979         // if it's been rendered remove dom node
49980         if(node.ui.rendered){
49981             node.ui.remove();
49982         }
49983         if(this.childNodes.length < 1){
49984             this.collapse(false, false);
49985         }else{
49986             this.ui.updateExpandIcon();
49987         }
49988         if(!this.firstChild && !this.isHiddenRoot()) {
49989             this.childrenRendered = false;
49990         }
49991         return node;
49992     },
49993
49994     // private override
49995     insertBefore : function(node, refNode){
49996         var newNode, exists;
49997         if(!node.render){
49998             node = this.getLoader().createNode(node);
49999         } else {
50000             exists = Ext.isObject(node.parentNode);
50001         }
50002         newNode = Ext.tree.TreeNode.superclass.insertBefore.call(this, node, refNode);
50003         if(newNode && refNode){
50004             this.afterAdd(newNode, exists);
50005         }
50006         this.ui.updateExpandIcon();
50007         return newNode;
50008     },
50009     
50010     // private
50011     afterAdd : function(node, exists){
50012         if(this.childrenRendered){
50013             // bulk render if the node already exists
50014             node.render(exists);
50015         }else if(exists){
50016             // make sure we update the indent
50017             node.renderIndent(true, true);
50018         }
50019     },
50020
50021     /**
50022      * Sets the text for this node
50023      * @param {String} text
50024      */
50025     setText : function(text){
50026         var oldText = this.text;
50027         this.text = this.attributes.text = text;
50028         if(this.rendered){ // event without subscribing
50029             this.ui.onTextChange(this, text, oldText);
50030         }
50031         this.fireEvent('textchange', this, text, oldText);
50032     },
50033
50034     /**
50035      * Triggers selection of this node
50036      */
50037     select : function(){
50038         var t = this.getOwnerTree();
50039         if(t){
50040             t.getSelectionModel().select(this);
50041         }
50042     },
50043
50044     /**
50045      * Triggers deselection of this node
50046      * @param {Boolean} silent (optional) True to stop selection change events from firing.
50047      */
50048     unselect : function(silent){
50049         var t = this.getOwnerTree();
50050         if(t){
50051             t.getSelectionModel().unselect(this, silent);
50052         }
50053     },
50054
50055     /**
50056      * Returns true if this node is selected
50057      * @return {Boolean}
50058      */
50059     isSelected : function(){
50060         var t = this.getOwnerTree();
50061         return t ? t.getSelectionModel().isSelected(this) : false;
50062     },
50063
50064     /**
50065      * Expand this node.
50066      * @param {Boolean} deep (optional) True to expand all children as well
50067      * @param {Boolean} anim (optional) false to cancel the default animation
50068      * @param {Function} callback (optional) A callback to be called when
50069      * expanding this node completes (does not wait for deep expand to complete).
50070      * Called with 1 parameter, this node.
50071      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the callback is executed. Defaults to this TreeNode.
50072      */
50073     expand : function(deep, anim, callback, scope){
50074         if(!this.expanded){
50075             if(this.fireEvent('beforeexpand', this, deep, anim) === false){
50076                 return;
50077             }
50078             if(!this.childrenRendered){
50079                 this.renderChildren();
50080             }
50081             this.expanded = true;
50082             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
50083                 this.ui.animExpand(function(){
50084                     this.fireEvent('expand', this);
50085                     this.runCallback(callback, scope || this, [this]);
50086                     if(deep === true){
50087                         this.expandChildNodes(true);
50088                     }
50089                 }.createDelegate(this));
50090                 return;
50091             }else{
50092                 this.ui.expand();
50093                 this.fireEvent('expand', this);
50094                 this.runCallback(callback, scope || this, [this]);
50095             }
50096         }else{
50097            this.runCallback(callback, scope || this, [this]);
50098         }
50099         if(deep === true){
50100             this.expandChildNodes(true);
50101         }
50102     },
50103
50104     runCallback : function(cb, scope, args){
50105         if(Ext.isFunction(cb)){
50106             cb.apply(scope, args);
50107         }
50108     },
50109
50110     isHiddenRoot : function(){
50111         return this.isRoot && !this.getOwnerTree().rootVisible;
50112     },
50113
50114     /**
50115      * Collapse this node.
50116      * @param {Boolean} deep (optional) True to collapse all children as well
50117      * @param {Boolean} anim (optional) false to cancel the default animation
50118      * @param {Function} callback (optional) A callback to be called when
50119      * expanding this node completes (does not wait for deep expand to complete).
50120      * Called with 1 parameter, this node.
50121      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the callback is executed. Defaults to this TreeNode.
50122      */
50123     collapse : function(deep, anim, callback, scope){
50124         if(this.expanded && !this.isHiddenRoot()){
50125             if(this.fireEvent('beforecollapse', this, deep, anim) === false){
50126                 return;
50127             }
50128             this.expanded = false;
50129             if((this.getOwnerTree().animate && anim !== false) || anim){
50130                 this.ui.animCollapse(function(){
50131                     this.fireEvent('collapse', this);
50132                     this.runCallback(callback, scope || this, [this]);
50133                     if(deep === true){
50134                         this.collapseChildNodes(true);
50135                     }
50136                 }.createDelegate(this));
50137                 return;
50138             }else{
50139                 this.ui.collapse();
50140                 this.fireEvent('collapse', this);
50141                 this.runCallback(callback, scope || this, [this]);
50142             }
50143         }else if(!this.expanded){
50144             this.runCallback(callback, scope || this, [this]);
50145         }
50146         if(deep === true){
50147             var cs = this.childNodes;
50148             for(var i = 0, len = cs.length; i < len; i++) {
50149                 cs[i].collapse(true, false);
50150             }
50151         }
50152     },
50153
50154     // private
50155     delayedExpand : function(delay){
50156         if(!this.expandProcId){
50157             this.expandProcId = this.expand.defer(delay, this);
50158         }
50159     },
50160
50161     // private
50162     cancelExpand : function(){
50163         if(this.expandProcId){
50164             clearTimeout(this.expandProcId);
50165         }
50166         this.expandProcId = false;
50167     },
50168
50169     /**
50170      * Toggles expanded/collapsed state of the node
50171      */
50172     toggle : function(){
50173         if(this.expanded){
50174             this.collapse();
50175         }else{
50176             this.expand();
50177         }
50178     },
50179
50180     /**
50181      * Ensures all parent nodes are expanded, and if necessary, scrolls
50182      * the node into view.
50183      * @param {Function} callback (optional) A function to call when the node has been made visible.
50184      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the callback is executed. Defaults to this TreeNode.
50185      */
50186     ensureVisible : function(callback, scope){
50187         var tree = this.getOwnerTree();
50188         tree.expandPath(this.parentNode ? this.parentNode.getPath() : this.getPath(), false, function(){
50189             var node = tree.getNodeById(this.id);  // Somehow if we don't do this, we lose changes that happened to node in the meantime
50190             tree.getTreeEl().scrollChildIntoView(node.ui.anchor);
50191             this.runCallback(callback, scope || this, [this]);
50192         }.createDelegate(this));
50193     },
50194
50195     /**
50196      * Expand all child nodes
50197      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
50198      */
50199     expandChildNodes : function(deep){
50200         var cs = this.childNodes;
50201         for(var i = 0, len = cs.length; i < len; i++) {
50202                 cs[i].expand(deep);
50203         }
50204     },
50205
50206     /**
50207      * Collapse all child nodes
50208      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
50209      */
50210     collapseChildNodes : function(deep){
50211         var cs = this.childNodes;
50212         for(var i = 0, len = cs.length; i < len; i++) {
50213                 cs[i].collapse(deep);
50214         }
50215     },
50216
50217     /**
50218      * Disables this node
50219      */
50220     disable : function(){
50221         this.disabled = true;
50222         this.unselect();
50223         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
50224             this.ui.onDisableChange(this, true);
50225         }
50226         this.fireEvent('disabledchange', this, true);
50227     },
50228
50229     /**
50230      * Enables this node
50231      */
50232     enable : function(){
50233         this.disabled = false;
50234         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
50235             this.ui.onDisableChange(this, false);
50236         }
50237         this.fireEvent('disabledchange', this, false);
50238     },
50239
50240     // private
50241     renderChildren : function(suppressEvent){
50242         if(suppressEvent !== false){
50243             this.fireEvent('beforechildrenrendered', this);
50244         }
50245         var cs = this.childNodes;
50246         for(var i = 0, len = cs.length; i < len; i++){
50247             cs[i].render(true);
50248         }
50249         this.childrenRendered = true;
50250     },
50251
50252     // private
50253     sort : function(fn, scope){
50254         Ext.tree.TreeNode.superclass.sort.apply(this, arguments);
50255         if(this.childrenRendered){
50256             var cs = this.childNodes;
50257             for(var i = 0, len = cs.length; i < len; i++){
50258                 cs[i].render(true);
50259             }
50260         }
50261     },
50262
50263     // private
50264     render : function(bulkRender){
50265         this.ui.render(bulkRender);
50266         if(!this.rendered){
50267             // make sure it is registered
50268             this.getOwnerTree().registerNode(this);
50269             this.rendered = true;
50270             if(this.expanded){
50271                 this.expanded = false;
50272                 this.expand(false, false);
50273             }
50274         }
50275     },
50276
50277     // private
50278     renderIndent : function(deep, refresh){
50279         if(refresh){
50280             this.ui.childIndent = null;
50281         }
50282         this.ui.renderIndent();
50283         if(deep === true && this.childrenRendered){
50284             var cs = this.childNodes;
50285             for(var i = 0, len = cs.length; i < len; i++){
50286                 cs[i].renderIndent(true, refresh);
50287             }
50288         }
50289     },
50290
50291     beginUpdate : function(){
50292         this.childrenRendered = false;
50293     },
50294
50295     endUpdate : function(){
50296         if(this.expanded && this.rendered){
50297             this.renderChildren();
50298         }
50299     },
50300
50301     destroy : function(){
50302         this.unselect(true);
50303         Ext.tree.TreeNode.superclass.destroy.call(this);
50304         Ext.destroy(this.ui, this.loader);
50305         this.ui = this.loader = null;
50306     },
50307
50308     // private
50309     onIdChange : function(id){
50310         this.ui.onIdChange(id);
50311     }
50312 });
50313
50314 Ext.tree.TreePanel.nodeTypes.node = Ext.tree.TreeNode;/**\r
50315  * @class Ext.tree.AsyncTreeNode\r
50316  * @extends Ext.tree.TreeNode\r
50317  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)\r
50318  * @constructor\r
50319  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node \r
50320  */\r
50321  Ext.tree.AsyncTreeNode = function(config){\r
50322     this.loaded = config && config.loaded === true;\r
50323     this.loading = false;\r
50324     Ext.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);\r
50325     /**\r
50326     * @event beforeload\r
50327     * Fires before this node is loaded, return false to cancel\r
50328     * @param {Node} this This node\r
50329     */\r
50330     this.addEvents('beforeload', 'load');\r
50331     /**\r
50332     * @event load\r
50333     * Fires when this node is loaded\r
50334     * @param {Node} this This node\r
50335     */\r
50336     /**\r
50337      * The loader used by this node (defaults to using the tree's defined loader)\r
50338      * @type TreeLoader\r
50339      * @property loader\r
50340      */\r
50341 };\r
50342 Ext.extend(Ext.tree.AsyncTreeNode, Ext.tree.TreeNode, {\r
50343     expand : function(deep, anim, callback, scope){\r
50344         if(this.loading){ // if an async load is already running, waiting til it's done\r
50345             var timer;\r
50346             var f = function(){\r
50347                 if(!this.loading){ // done loading\r
50348                     clearInterval(timer);\r
50349                     this.expand(deep, anim, callback, scope);\r
50350                 }\r
50351             }.createDelegate(this);\r
50352             timer = setInterval(f, 200);\r
50353             return;\r
50354         }\r
50355         if(!this.loaded){\r
50356             if(this.fireEvent("beforeload", this) === false){\r
50357                 return;\r
50358             }\r
50359             this.loading = true;\r
50360             this.ui.beforeLoad(this);\r
50361             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();\r
50362             if(loader){\r
50363                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback, scope]), this);\r
50364                 return;\r
50365             }\r
50366         }\r
50367         Ext.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback, scope);\r
50368     },\r
50369     \r
50370     /**\r
50371      * Returns true if this node is currently loading\r
50372      * @return {Boolean}\r
50373      */\r
50374     isLoading : function(){\r
50375         return this.loading;  \r
50376     },\r
50377     \r
50378     loadComplete : function(deep, anim, callback, scope){\r
50379         this.loading = false;\r
50380         this.loaded = true;\r
50381         this.ui.afterLoad(this);\r
50382         this.fireEvent("load", this);\r
50383         this.expand(deep, anim, callback, scope);\r
50384     },\r
50385     \r
50386     /**\r
50387      * Returns true if this node has been loaded\r
50388      * @return {Boolean}\r
50389      */\r
50390     isLoaded : function(){\r
50391         return this.loaded;\r
50392     },\r
50393     \r
50394     hasChildNodes : function(){\r
50395         if(!this.isLeaf() && !this.loaded){\r
50396             return true;\r
50397         }else{\r
50398             return Ext.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);\r
50399         }\r
50400     },\r
50401 \r
50402     /**\r
50403      * Trigger a reload for this node\r
50404      * @param {Function} callback\r
50405      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the callback is executed. Defaults to this Node.\r
50406      */\r
50407     reload : function(callback, scope){\r
50408         this.collapse(false, false);\r
50409         while(this.firstChild){\r
50410             this.removeChild(this.firstChild).destroy();\r
50411         }\r
50412         this.childrenRendered = false;\r
50413         this.loaded = false;\r
50414         if(this.isHiddenRoot()){\r
50415             this.expanded = false;\r
50416         }\r
50417         this.expand(false, false, callback, scope);\r
50418     }\r
50419 });\r
50420 \r
50421 Ext.tree.TreePanel.nodeTypes.async = Ext.tree.AsyncTreeNode;/**\r
50422  * @class Ext.tree.TreeNodeUI\r
50423  * This class provides the default UI implementation for Ext TreeNodes.\r
50424  * The TreeNode UI implementation is separate from the\r
50425  * tree implementation, and allows customizing of the appearance of\r
50426  * tree nodes.<br>\r
50427  * <p>\r
50428  * If you are customizing the Tree's user interface, you\r
50429  * may need to extend this class, but you should never need to instantiate this class.<br>\r
50430  * <p>\r
50431  * This class provides access to the user interface components of an Ext TreeNode, through\r
50432  * {@link Ext.tree.TreeNode#getUI}\r
50433  */\r
50434 Ext.tree.TreeNodeUI = function(node){\r
50435     this.node = node;\r
50436     this.rendered = false;\r
50437     this.animating = false;\r
50438     this.wasLeaf = true;\r
50439     this.ecc = 'x-tree-ec-icon x-tree-elbow';\r
50440     this.emptyIcon = Ext.BLANK_IMAGE_URL;\r
50441 };\r
50442 \r
50443 Ext.tree.TreeNodeUI.prototype = {\r
50444     // private\r
50445     removeChild : function(node){\r
50446         if(this.rendered){\r
50447             this.ctNode.removeChild(node.ui.getEl());\r
50448         }\r
50449     },\r
50450 \r
50451     // private\r
50452     beforeLoad : function(){\r
50453          this.addClass("x-tree-node-loading");\r
50454     },\r
50455 \r
50456     // private\r
50457     afterLoad : function(){\r
50458          this.removeClass("x-tree-node-loading");\r
50459     },\r
50460 \r
50461     // private\r
50462     onTextChange : function(node, text, oldText){\r
50463         if(this.rendered){\r
50464             this.textNode.innerHTML = text;\r
50465         }\r
50466     },\r
50467 \r
50468     // private\r
50469     onDisableChange : function(node, state){\r
50470         this.disabled = state;\r
50471         if (this.checkbox) {\r
50472             this.checkbox.disabled = state;\r
50473         }\r
50474         if(state){\r
50475             this.addClass("x-tree-node-disabled");\r
50476         }else{\r
50477             this.removeClass("x-tree-node-disabled");\r
50478         }\r
50479     },\r
50480 \r
50481     // private\r
50482     onSelectedChange : function(state){\r
50483         if(state){\r
50484             this.focus();\r
50485             this.addClass("x-tree-selected");\r
50486         }else{\r
50487             //this.blur();\r
50488             this.removeClass("x-tree-selected");\r
50489         }\r
50490     },\r
50491 \r
50492     // private\r
50493     onMove : function(tree, node, oldParent, newParent, index, refNode){\r
50494         this.childIndent = null;\r
50495         if(this.rendered){\r
50496             var targetNode = newParent.ui.getContainer();\r
50497             if(!targetNode){//target not rendered\r
50498                 this.holder = document.createElement("div");\r
50499                 this.holder.appendChild(this.wrap);\r
50500                 return;\r
50501             }\r
50502             var insertBefore = refNode ? refNode.ui.getEl() : null;\r
50503             if(insertBefore){\r
50504                 targetNode.insertBefore(this.wrap, insertBefore);\r
50505             }else{\r
50506                 targetNode.appendChild(this.wrap);\r
50507             }\r
50508             this.node.renderIndent(true, oldParent != newParent);\r
50509         }\r
50510     },\r
50511 \r
50512 /**\r
50513  * Adds one or more CSS classes to the node's UI element.\r
50514  * Duplicate classes are automatically filtered out.\r
50515  * @param {String/Array} className The CSS class to add, or an array of classes\r
50516  */\r
50517     addClass : function(cls){\r
50518         if(this.elNode){\r
50519             Ext.fly(this.elNode).addClass(cls);\r
50520         }\r
50521     },\r
50522 \r
50523 /**\r
50524  * Removes one or more CSS classes from the node's UI element.\r
50525  * @param {String/Array} className The CSS class to remove, or an array of classes\r
50526  */\r
50527     removeClass : function(cls){\r
50528         if(this.elNode){\r
50529             Ext.fly(this.elNode).removeClass(cls);\r
50530         }\r
50531     },\r
50532 \r
50533     // private\r
50534     remove : function(){\r
50535         if(this.rendered){\r
50536             this.holder = document.createElement("div");\r
50537             this.holder.appendChild(this.wrap);\r
50538         }\r
50539     },\r
50540 \r
50541     // private\r
50542     fireEvent : function(){\r
50543         return this.node.fireEvent.apply(this.node, arguments);\r
50544     },\r
50545 \r
50546     // private\r
50547     initEvents : function(){\r
50548         this.node.on("move", this.onMove, this);\r
50549 \r
50550         if(this.node.disabled){\r
50551             this.onDisableChange(this.node, true);\r
50552         }\r
50553         if(this.node.hidden){\r
50554             this.hide();\r
50555         }\r
50556         var ot = this.node.getOwnerTree();\r
50557         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;\r
50558         if(dd && (!this.node.isRoot || ot.rootVisible)){\r
50559             Ext.dd.Registry.register(this.elNode, {\r
50560                 node: this.node,\r
50561                 handles: this.getDDHandles(),\r
50562                 isHandle: false\r
50563             });\r
50564         }\r
50565     },\r
50566 \r
50567     // private\r
50568     getDDHandles : function(){\r
50569         return [this.iconNode, this.textNode, this.elNode];\r
50570     },\r
50571 \r
50572 /**\r
50573  * Hides this node.\r
50574  */\r
50575     hide : function(){\r
50576         this.node.hidden = true;\r
50577         if(this.wrap){\r
50578             this.wrap.style.display = "none";\r
50579         }\r
50580     },\r
50581 \r
50582 /**\r
50583  * Shows this node.\r
50584  */\r
50585     show : function(){\r
50586         this.node.hidden = false;\r
50587         if(this.wrap){\r
50588             this.wrap.style.display = "";\r
50589         }\r
50590     },\r
50591 \r
50592     // private\r
50593     onContextMenu : function(e){\r
50594         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {\r
50595             e.preventDefault();\r
50596             this.focus();\r
50597             this.fireEvent("contextmenu", this.node, e);\r
50598         }\r
50599     },\r
50600 \r
50601     // private\r
50602     onClick : function(e){\r
50603         if(this.dropping){\r
50604             e.stopEvent();\r
50605             return;\r
50606         }\r
50607         if(this.fireEvent("beforeclick", this.node, e) !== false){\r
50608             var a = e.getTarget('a');\r
50609             if(!this.disabled && this.node.attributes.href && a){\r
50610                 this.fireEvent("click", this.node, e);\r
50611                 return;\r
50612             }else if(a && e.ctrlKey){\r
50613                 e.stopEvent();\r
50614             }\r
50615             e.preventDefault();\r
50616             if(this.disabled){\r
50617                 return;\r
50618             }\r
50619 \r
50620             if(this.node.attributes.singleClickExpand && !this.animating && this.node.isExpandable()){\r
50621                 this.node.toggle();\r
50622             }\r
50623 \r
50624             this.fireEvent("click", this.node, e);\r
50625         }else{\r
50626             e.stopEvent();\r
50627         }\r
50628     },\r
50629 \r
50630     // private\r
50631     onDblClick : function(e){\r
50632         e.preventDefault();\r
50633         if(this.disabled){\r
50634             return;\r
50635         }\r
50636         if(this.fireEvent("beforedblclick", this.node, e) !== false){\r
50637             if(this.checkbox){\r
50638                 this.toggleCheck();\r
50639             }\r
50640             if(!this.animating && this.node.isExpandable()){\r
50641                 this.node.toggle();\r
50642             }\r
50643             this.fireEvent("dblclick", this.node, e);\r
50644         }\r
50645     },\r
50646 \r
50647     onOver : function(e){\r
50648         this.addClass('x-tree-node-over');\r
50649     },\r
50650 \r
50651     onOut : function(e){\r
50652         this.removeClass('x-tree-node-over');\r
50653     },\r
50654 \r
50655     // private\r
50656     onCheckChange : function(){\r
50657         var checked = this.checkbox.checked;\r
50658         // fix for IE6\r
50659         this.checkbox.defaultChecked = checked;\r
50660         this.node.attributes.checked = checked;\r
50661         this.fireEvent('checkchange', this.node, checked);\r
50662     },\r
50663 \r
50664     // private\r
50665     ecClick : function(e){\r
50666         if(!this.animating && this.node.isExpandable()){\r
50667             this.node.toggle();\r
50668         }\r
50669     },\r
50670 \r
50671     // private\r
50672     startDrop : function(){\r
50673         this.dropping = true;\r
50674     },\r
50675 \r
50676     // delayed drop so the click event doesn't get fired on a drop\r
50677     endDrop : function(){\r
50678        setTimeout(function(){\r
50679            this.dropping = false;\r
50680        }.createDelegate(this), 50);\r
50681     },\r
50682 \r
50683     // private\r
50684     expand : function(){\r
50685         this.updateExpandIcon();\r
50686         this.ctNode.style.display = "";\r
50687     },\r
50688 \r
50689     // private\r
50690     focus : function(){\r
50691         if(!this.node.preventHScroll){\r
50692             try{this.anchor.focus();\r
50693             }catch(e){}\r
50694         }else{\r
50695             try{\r
50696                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;\r
50697                 var l = noscroll.scrollLeft;\r
50698                 this.anchor.focus();\r
50699                 noscroll.scrollLeft = l;\r
50700             }catch(e){}\r
50701         }\r
50702     },\r
50703 \r
50704 /**\r
50705  * Sets the checked status of the tree node to the passed value, or, if no value was passed,\r
50706  * toggles the checked status. If the node was rendered with no checkbox, this has no effect.\r
50707  * @param {Boolean} (optional) The new checked status.\r
50708  */\r
50709     toggleCheck : function(value){\r
50710         var cb = this.checkbox;\r
50711         if(cb){\r
50712             cb.checked = (value === undefined ? !cb.checked : value);\r
50713             this.onCheckChange();\r
50714         }\r
50715     },\r
50716 \r
50717     // private\r
50718     blur : function(){\r
50719         try{\r
50720             this.anchor.blur();\r
50721         }catch(e){}\r
50722     },\r
50723 \r
50724     // private\r
50725     animExpand : function(callback){\r
50726         var ct = Ext.get(this.ctNode);\r
50727         ct.stopFx();\r
50728         if(!this.node.isExpandable()){\r
50729             this.updateExpandIcon();\r
50730             this.ctNode.style.display = "";\r
50731             Ext.callback(callback);\r
50732             return;\r
50733         }\r
50734         this.animating = true;\r
50735         this.updateExpandIcon();\r
50736 \r
50737         ct.slideIn('t', {\r
50738            callback : function(){\r
50739                this.animating = false;\r
50740                Ext.callback(callback);\r
50741             },\r
50742             scope: this,\r
50743             duration: this.node.ownerTree.duration || .25\r
50744         });\r
50745     },\r
50746 \r
50747     // private\r
50748     highlight : function(){\r
50749         var tree = this.node.getOwnerTree();\r
50750         Ext.fly(this.wrap).highlight(\r
50751             tree.hlColor || "C3DAF9",\r
50752             {endColor: tree.hlBaseColor}\r
50753         );\r
50754     },\r
50755 \r
50756     // private\r
50757     collapse : function(){\r
50758         this.updateExpandIcon();\r
50759         this.ctNode.style.display = "none";\r
50760     },\r
50761 \r
50762     // private\r
50763     animCollapse : function(callback){\r
50764         var ct = Ext.get(this.ctNode);\r
50765         ct.enableDisplayMode('block');\r
50766         ct.stopFx();\r
50767 \r
50768         this.animating = true;\r
50769         this.updateExpandIcon();\r
50770 \r
50771         ct.slideOut('t', {\r
50772             callback : function(){\r
50773                this.animating = false;\r
50774                Ext.callback(callback);\r
50775             },\r
50776             scope: this,\r
50777             duration: this.node.ownerTree.duration || .25\r
50778         });\r
50779     },\r
50780 \r
50781     // private\r
50782     getContainer : function(){\r
50783         return this.ctNode;\r
50784     },\r
50785 \r
50786 /**\r
50787  * Returns the element which encapsulates this node.\r
50788  * @return {HtmlElement} The DOM element. The default implementation uses a <code>&lt;li></code>.\r
50789  */\r
50790     getEl : function(){\r
50791         return this.wrap;\r
50792     },\r
50793 \r
50794     // private\r
50795     appendDDGhost : function(ghostNode){\r
50796         ghostNode.appendChild(this.elNode.cloneNode(true));\r
50797     },\r
50798 \r
50799     // private\r
50800     getDDRepairXY : function(){\r
50801         return Ext.lib.Dom.getXY(this.iconNode);\r
50802     },\r
50803 \r
50804     // private\r
50805     onRender : function(){\r
50806         this.render();\r
50807     },\r
50808 \r
50809     // private\r
50810     render : function(bulkRender){\r
50811         var n = this.node, a = n.attributes;\r
50812         var targetNode = n.parentNode ?\r
50813               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;\r
50814 \r
50815         if(!this.rendered){\r
50816             this.rendered = true;\r
50817 \r
50818             this.renderElements(n, a, targetNode, bulkRender);\r
50819 \r
50820             if(a.qtip){\r
50821                if(this.textNode.setAttributeNS){\r
50822                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);\r
50823                    if(a.qtipTitle){\r
50824                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);\r
50825                    }\r
50826                }else{\r
50827                    this.textNode.setAttribute("ext:qtip", a.qtip);\r
50828                    if(a.qtipTitle){\r
50829                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);\r
50830                    }\r
50831                }\r
50832             }else if(a.qtipCfg){\r
50833                 a.qtipCfg.target = Ext.id(this.textNode);\r
50834                 Ext.QuickTips.register(a.qtipCfg);\r
50835             }\r
50836             this.initEvents();\r
50837             if(!this.node.expanded){\r
50838                 this.updateExpandIcon(true);\r
50839             }\r
50840         }else{\r
50841             if(bulkRender === true) {\r
50842                 targetNode.appendChild(this.wrap);\r
50843             }\r
50844         }\r
50845     },\r
50846 \r
50847     // private\r
50848     renderElements : function(n, a, targetNode, bulkRender){\r
50849         // add some indent caching, this helps performance when rendering a large tree\r
50850         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';\r
50851 \r
50852         var cb = Ext.isBoolean(a.checked),\r
50853             nel,\r
50854             href = a.href ? a.href : Ext.isGecko ? "" : "#",\r
50855             buf = ['<li class="x-tree-node"><div ext:tree-node-id="',n.id,'" class="x-tree-node-el x-tree-node-leaf x-unselectable ', a.cls,'" unselectable="on">',\r
50856             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",\r
50857             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon x-tree-elbow" />',\r
50858             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',\r
50859             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : '/>')) : '',\r
50860             '<a hidefocus="on" class="x-tree-node-anchor" href="',href,'" tabIndex="1" ',\r
50861              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", '><span unselectable="on">',n.text,"</span></a></div>",\r
50862             '<ul class="x-tree-node-ct" style="display:none;"></ul>',\r
50863             "</li>"].join('');\r
50864 \r
50865         if(bulkRender !== true && n.nextSibling && (nel = n.nextSibling.ui.getEl())){\r
50866             this.wrap = Ext.DomHelper.insertHtml("beforeBegin", nel, buf);\r
50867         }else{\r
50868             this.wrap = Ext.DomHelper.insertHtml("beforeEnd", targetNode, buf);\r
50869         }\r
50870 \r
50871         this.elNode = this.wrap.childNodes[0];\r
50872         this.ctNode = this.wrap.childNodes[1];\r
50873         var cs = this.elNode.childNodes;\r
50874         this.indentNode = cs[0];\r
50875         this.ecNode = cs[1];\r
50876         this.iconNode = cs[2];\r
50877         var index = 3;\r
50878         if(cb){\r
50879             this.checkbox = cs[3];\r
50880             // fix for IE6\r
50881             this.checkbox.defaultChecked = this.checkbox.checked;\r
50882             index++;\r
50883         }\r
50884         this.anchor = cs[index];\r
50885         this.textNode = cs[index].firstChild;\r
50886     },\r
50887 \r
50888 /**\r
50889  * Returns the &lt;a> element that provides focus for the node's UI.\r
50890  * @return {HtmlElement} The DOM anchor element.\r
50891  */\r
50892     getAnchor : function(){\r
50893         return this.anchor;\r
50894     },\r
50895 \r
50896 /**\r
50897  * Returns the text node.\r
50898  * @return {HtmlNode} The DOM text node.\r
50899  */\r
50900     getTextEl : function(){\r
50901         return this.textNode;\r
50902     },\r
50903 \r
50904 /**\r
50905  * Returns the icon &lt;img> element.\r
50906  * @return {HtmlElement} The DOM image element.\r
50907  */\r
50908     getIconEl : function(){\r
50909         return this.iconNode;\r
50910     },\r
50911 \r
50912 /**\r
50913  * Returns the checked status of the node. If the node was rendered with no\r
50914  * checkbox, it returns false.\r
50915  * @return {Boolean} The checked flag.\r
50916  */\r
50917     isChecked : function(){\r
50918         return this.checkbox ? this.checkbox.checked : false;\r
50919     },\r
50920 \r
50921     // private\r
50922     updateExpandIcon : function(){\r
50923         if(this.rendered){\r
50924             var n = this.node,\r
50925                 c1,\r
50926                 c2,\r
50927                 cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow",\r
50928                 hasChild = n.hasChildNodes();\r
50929             if(hasChild || n.attributes.expandable){\r
50930                 if(n.expanded){\r
50931                     cls += "-minus";\r
50932                     c1 = "x-tree-node-collapsed";\r
50933                     c2 = "x-tree-node-expanded";\r
50934                 }else{\r
50935                     cls += "-plus";\r
50936                     c1 = "x-tree-node-expanded";\r
50937                     c2 = "x-tree-node-collapsed";\r
50938                 }\r
50939                 if(this.wasLeaf){\r
50940                     this.removeClass("x-tree-node-leaf");\r
50941                     this.wasLeaf = false;\r
50942                 }\r
50943                 if(this.c1 != c1 || this.c2 != c2){\r
50944                     Ext.fly(this.elNode).replaceClass(c1, c2);\r
50945                     this.c1 = c1; this.c2 = c2;\r
50946                 }\r
50947             }else{\r
50948                 if(!this.wasLeaf){\r
50949                     Ext.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-collapsed");\r
50950                     delete this.c1;\r
50951                     delete this.c2;\r
50952                     this.wasLeaf = true;\r
50953                 }\r
50954             }\r
50955             var ecc = "x-tree-ec-icon "+cls;\r
50956             if(this.ecc != ecc){\r
50957                 this.ecNode.className = ecc;\r
50958                 this.ecc = ecc;\r
50959             }\r
50960         }\r
50961     },\r
50962 \r
50963     // private\r
50964     onIdChange: function(id){\r
50965         if(this.rendered){\r
50966             this.elNode.setAttribute('ext:tree-node-id', id);\r
50967         }\r
50968     },\r
50969 \r
50970     // private\r
50971     getChildIndent : function(){\r
50972         if(!this.childIndent){\r
50973             var buf = [],\r
50974                 p = this.node;\r
50975             while(p){\r
50976                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){\r
50977                     if(!p.isLast()) {\r
50978                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');\r
50979                     } else {\r
50980                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');\r
50981                     }\r
50982                 }\r
50983                 p = p.parentNode;\r
50984             }\r
50985             this.childIndent = buf.join("");\r
50986         }\r
50987         return this.childIndent;\r
50988     },\r
50989 \r
50990     // private\r
50991     renderIndent : function(){\r
50992         if(this.rendered){\r
50993             var indent = "",\r
50994                 p = this.node.parentNode;\r
50995             if(p){\r
50996                 indent = p.ui.getChildIndent();\r
50997             }\r
50998             if(this.indentMarkup != indent){ // don't rerender if not required\r
50999                 this.indentNode.innerHTML = indent;\r
51000                 this.indentMarkup = indent;\r
51001             }\r
51002             this.updateExpandIcon();\r
51003         }\r
51004     },\r
51005 \r
51006     destroy : function(){\r
51007         if(this.elNode){\r
51008             Ext.dd.Registry.unregister(this.elNode.id);\r
51009         }\r
51010 \r
51011         Ext.each(['textnode', 'anchor', 'checkbox', 'indentNode', 'ecNode', 'iconNode', 'elNode', 'ctNode', 'wrap', 'holder'], function(el){\r
51012             if(this[el]){\r
51013                 Ext.fly(this[el]).remove();\r
51014                 delete this[el];\r
51015             }\r
51016         }, this);\r
51017         delete this.node;\r
51018     }\r
51019 };\r
51020 \r
51021 /**\r
51022  * @class Ext.tree.RootTreeNodeUI\r
51023  * This class provides the default UI implementation for <b>root</b> Ext TreeNodes.\r
51024  * The RootTreeNode UI implementation allows customizing the appearance of the root tree node.<br>\r
51025  * <p>\r
51026  * If you are customizing the Tree's user interface, you\r
51027  * may need to extend this class, but you should never need to instantiate this class.<br>\r
51028  */\r
51029 Ext.tree.RootTreeNodeUI = Ext.extend(Ext.tree.TreeNodeUI, {\r
51030     // private\r
51031     render : function(){\r
51032         if(!this.rendered){\r
51033             var targetNode = this.node.ownerTree.innerCt.dom;\r
51034             this.node.expanded = true;\r
51035             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';\r
51036             this.wrap = this.ctNode = targetNode.firstChild;\r
51037         }\r
51038     },\r
51039     collapse : Ext.emptyFn,\r
51040     expand : Ext.emptyFn\r
51041 });/**\r
51042  * @class Ext.tree.TreeLoader\r
51043  * @extends Ext.util.Observable\r
51044  * A TreeLoader provides for lazy loading of an {@link Ext.tree.TreeNode}'s child\r
51045  * nodes from a specified URL. The response must be a JavaScript Array definition\r
51046  * whose elements are node definition objects. e.g.:\r
51047  * <pre><code>\r
51048     [{\r
51049         id: 1,\r
51050         text: 'A leaf Node',\r
51051         leaf: true\r
51052     },{\r
51053         id: 2,\r
51054         text: 'A folder Node',\r
51055         children: [{\r
51056             id: 3,\r
51057             text: 'A child Node',\r
51058             leaf: true\r
51059         }]\r
51060    }]\r
51061 </code></pre>\r
51062  * <br><br>\r
51063  * A server request is sent, and child nodes are loaded only when a node is expanded.\r
51064  * The loading node's id is passed to the server under the parameter name "node" to\r
51065  * enable the server to produce the correct child nodes.\r
51066  * <br><br>\r
51067  * To pass extra parameters, an event handler may be attached to the "beforeload"\r
51068  * event, and the parameters specified in the TreeLoader's baseParams property:\r
51069  * <pre><code>\r
51070     myTreeLoader.on("beforeload", function(treeLoader, node) {\r
51071         this.baseParams.category = node.attributes.category;\r
51072     }, this);\r
51073 </code></pre>\r
51074  * This would pass an HTTP parameter called "category" to the server containing\r
51075  * the value of the Node's "category" attribute.\r
51076  * @constructor\r
51077  * Creates a new Treeloader.\r
51078  * @param {Object} config A config object containing config properties.\r
51079  */\r
51080 Ext.tree.TreeLoader = function(config){\r
51081     this.baseParams = {};\r
51082     Ext.apply(this, config);\r
51083 \r
51084     this.addEvents(\r
51085         /**\r
51086          * @event beforeload\r
51087          * Fires before a network request is made to retrieve the Json text which specifies a node's children.\r
51088          * @param {Object} This TreeLoader object.\r
51089          * @param {Object} node The {@link Ext.tree.TreeNode} object being loaded.\r
51090          * @param {Object} callback The callback function specified in the {@link #load} call.\r
51091          */\r
51092         "beforeload",\r
51093         /**\r
51094          * @event load\r
51095          * Fires when the node has been successfuly loaded.\r
51096          * @param {Object} This TreeLoader object.\r
51097          * @param {Object} node The {@link Ext.tree.TreeNode} object being loaded.\r
51098          * @param {Object} response The response object containing the data from the server.\r
51099          */\r
51100         "load",\r
51101         /**\r
51102          * @event loadexception\r
51103          * Fires if the network request failed.\r
51104          * @param {Object} This TreeLoader object.\r
51105          * @param {Object} node The {@link Ext.tree.TreeNode} object being loaded.\r
51106          * @param {Object} response The response object containing the data from the server.\r
51107          */\r
51108         "loadexception"\r
51109     );\r
51110     Ext.tree.TreeLoader.superclass.constructor.call(this);\r
51111     if(Ext.isString(this.paramOrder)){\r
51112         this.paramOrder = this.paramOrder.split(/[\s,|]/);\r
51113     }\r
51114 };\r
51115 \r
51116 Ext.extend(Ext.tree.TreeLoader, Ext.util.Observable, {\r
51117     /**\r
51118     * @cfg {String} dataUrl The URL from which to request a Json string which\r
51119     * specifies an array of node definition objects representing the child nodes\r
51120     * to be loaded.\r
51121     */\r
51122     /**\r
51123      * @cfg {String} requestMethod The HTTP request method for loading data (defaults to the value of {@link Ext.Ajax#method}).\r
51124      */\r
51125     /**\r
51126      * @cfg {String} url Equivalent to {@link #dataUrl}.\r
51127      */\r
51128     /**\r
51129      * @cfg {Boolean} preloadChildren If set to true, the loader recursively loads "children" attributes when doing the first load on nodes.\r
51130      */\r
51131     /**\r
51132     * @cfg {Object} baseParams (optional) An object containing properties which\r
51133     * specify HTTP parameters to be passed to each request for child nodes.\r
51134     */\r
51135     /**\r
51136     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes\r
51137     * created by this loader. If the attributes sent by the server have an attribute in this object,\r
51138     * they take priority.\r
51139     */\r
51140     /**\r
51141     * @cfg {Object} uiProviders (optional) An object containing properties which\r
51142     * specify custom {@link Ext.tree.TreeNodeUI} implementations. If the optional\r
51143     * <i>uiProvider</i> attribute of a returned child node is a string rather\r
51144     * than a reference to a TreeNodeUI implementation, then that string value\r
51145     * is used as a property name in the uiProviders object.\r
51146     */\r
51147     uiProviders : {},\r
51148 \r
51149     /**\r
51150     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing\r
51151     * child nodes before loading.\r
51152     */\r
51153     clearOnLoad : true,\r
51154 \r
51155     /**\r
51156      * @cfg {Array/String} paramOrder Defaults to <tt>undefined</tt>. Only used when using directFn.\r
51157      * Specifies the params in the order in which they must be passed to the server-side Direct method\r
51158      * as either (1) an Array of String values, or (2) a String of params delimited by either whitespace,\r
51159      * comma, or pipe. For example,\r
51160      * any of the following would be acceptable:<pre><code>\r
51161 nodeParameter: 'node',\r
51162 paramOrder: ['param1','param2','param3']\r
51163 paramOrder: 'node param1 param2 param3'\r
51164 paramOrder: 'param1,node,param2,param3'\r
51165 paramOrder: 'param1|param2|param|node'\r
51166      </code></pre>\r
51167      */\r
51168     paramOrder: undefined,\r
51169 \r
51170     /**\r
51171      * @cfg {Boolean} paramsAsHash Only used when using directFn.\r
51172      * Send parameters as a collection of named arguments (defaults to <tt>false</tt>). Providing a\r
51173      * <tt>{@link #paramOrder}</tt> nullifies this configuration.\r
51174      */\r
51175     paramsAsHash: false,\r
51176 \r
51177     /**\r
51178      * @cfg {String} nodeParameter The name of the parameter sent to the server which contains\r
51179      * the identifier of the node. Defaults to <tt>'node'</tt>.\r
51180      */\r
51181     nodeParameter: 'node',\r
51182 \r
51183     /**\r
51184      * @cfg {Function} directFn\r
51185      * Function to call when executing a request.\r
51186      */\r
51187     directFn : undefined,\r
51188 \r
51189     /**\r
51190      * Load an {@link Ext.tree.TreeNode} from the URL specified in the constructor.\r
51191      * This is called automatically when a node is expanded, but may be used to reload\r
51192      * a node (or append new children if the {@link #clearOnLoad} option is false.)\r
51193      * @param {Ext.tree.TreeNode} node\r
51194      * @param {Function} callback Function to call after the node has been loaded. The\r
51195      * function is passed the TreeNode which was requested to be loaded.\r
51196      * @param (Object) scope The cope (<code>this</code> reference) in which the callback is executed.\r
51197      * defaults to the loaded TreeNode.\r
51198      */\r
51199     load : function(node, callback, scope){\r
51200         if(this.clearOnLoad){\r
51201             while(node.firstChild){\r
51202                 node.removeChild(node.firstChild);\r
51203             }\r
51204         }\r
51205         if(this.doPreload(node)){ // preloaded json children\r
51206             this.runCallback(callback, scope || node, [node]);\r
51207         }else if(this.directFn || this.dataUrl || this.url){\r
51208             this.requestData(node, callback, scope || node);\r
51209         }\r
51210     },\r
51211 \r
51212     doPreload : function(node){\r
51213         if(node.attributes.children){\r
51214             if(node.childNodes.length < 1){ // preloaded?\r
51215                 var cs = node.attributes.children;\r
51216                 node.beginUpdate();\r
51217                 for(var i = 0, len = cs.length; i < len; i++){\r
51218                     var cn = node.appendChild(this.createNode(cs[i]));\r
51219                     if(this.preloadChildren){\r
51220                         this.doPreload(cn);\r
51221                     }\r
51222                 }\r
51223                 node.endUpdate();\r
51224             }\r
51225             return true;\r
51226         }\r
51227         return false;\r
51228     },\r
51229 \r
51230     getParams: function(node){\r
51231         var bp = Ext.apply({}, this.baseParams),\r
51232             np = this.nodeParameter,\r
51233             po = this.paramOrder;\r
51234 \r
51235         np && (bp[ np ] = node.id);\r
51236 \r
51237         if(this.directFn){\r
51238             var buf = [node.id];\r
51239             if(po){\r
51240                 // reset 'buf' if the nodeParameter was included in paramOrder\r
51241                 if(np && po.indexOf(np) > -1){\r
51242                     buf = [];\r
51243                 }\r
51244 \r
51245                 for(var i = 0, len = po.length; i < len; i++){\r
51246                     buf.push(bp[ po[i] ]);\r
51247                 }\r
51248             }else if(this.paramsAsHash){\r
51249                 buf = [bp];\r
51250             }\r
51251             return buf;\r
51252         }else{\r
51253             return bp;\r
51254         }\r
51255     },\r
51256 \r
51257     requestData : function(node, callback, scope){\r
51258         if(this.fireEvent("beforeload", this, node, callback) !== false){\r
51259             if(this.directFn){\r
51260                 var args = this.getParams(node);\r
51261                 args.push(this.processDirectResponse.createDelegate(this, [{callback: callback, node: node, scope: scope}], true));\r
51262                 this.directFn.apply(window, args);\r
51263             }else{\r
51264                 this.transId = Ext.Ajax.request({\r
51265                     method:this.requestMethod,\r
51266                     url: this.dataUrl||this.url,\r
51267                     success: this.handleResponse,\r
51268                     failure: this.handleFailure,\r
51269                     scope: this,\r
51270                     argument: {callback: callback, node: node, scope: scope},\r
51271                     params: this.getParams(node)\r
51272                 });\r
51273             }\r
51274         }else{\r
51275             // if the load is cancelled, make sure we notify\r
51276             // the node that we are done\r
51277             this.runCallback(callback, scope || node, []);\r
51278         }\r
51279     },\r
51280 \r
51281     processDirectResponse: function(result, response, args){\r
51282         if(response.status){\r
51283             this.handleResponse({\r
51284                 responseData: Ext.isArray(result) ? result : null,\r
51285                 responseText: result,\r
51286                 argument: args\r
51287             });\r
51288         }else{\r
51289             this.handleFailure({\r
51290                 argument: args\r
51291             });\r
51292         }\r
51293     },\r
51294 \r
51295     // private\r
51296     runCallback: function(cb, scope, args){\r
51297         if(Ext.isFunction(cb)){\r
51298             cb.apply(scope, args);\r
51299         }\r
51300     },\r
51301 \r
51302     isLoading : function(){\r
51303         return !!this.transId;\r
51304     },\r
51305 \r
51306     abort : function(){\r
51307         if(this.isLoading()){\r
51308             Ext.Ajax.abort(this.transId);\r
51309         }\r
51310     },\r
51311 \r
51312     /**\r
51313     * <p>Override this function for custom TreeNode node implementation, or to\r
51314     * modify the attributes at creation time.</p>\r
51315     * Example:<pre><code>\r
51316 new Ext.tree.TreePanel({\r
51317     ...\r
51318     loader: new Ext.tree.TreeLoader({\r
51319         url: 'dataUrl',\r
51320         createNode: function(attr) {\r
51321 //          Allow consolidation consignments to have\r
51322 //          consignments dropped into them.\r
51323             if (attr.isConsolidation) {\r
51324                 attr.iconCls = 'x-consol',\r
51325                 attr.allowDrop = true;\r
51326             }\r
51327             return Ext.tree.TreeLoader.prototype.createNode.call(this, attr);\r
51328         }\r
51329     }),\r
51330     ...\r
51331 });\r
51332 </code></pre>\r
51333     * @param attr {Object} The attributes from which to create the new node.\r
51334     */\r
51335     createNode : function(attr){\r
51336         // apply baseAttrs, nice idea Corey!\r
51337         if(this.baseAttrs){\r
51338             Ext.applyIf(attr, this.baseAttrs);\r
51339         }\r
51340         if(this.applyLoader !== false && !attr.loader){\r
51341             attr.loader = this;\r
51342         }\r
51343         if(Ext.isString(attr.uiProvider)){\r
51344            attr.uiProvider = this.uiProviders[attr.uiProvider] || eval(attr.uiProvider);\r
51345         }\r
51346         if(attr.nodeType){\r
51347             return new Ext.tree.TreePanel.nodeTypes[attr.nodeType](attr);\r
51348         }else{\r
51349             return attr.leaf ?\r
51350                         new Ext.tree.TreeNode(attr) :\r
51351                         new Ext.tree.AsyncTreeNode(attr);\r
51352         }\r
51353     },\r
51354 \r
51355     processResponse : function(response, node, callback, scope){\r
51356         var json = response.responseText;\r
51357         try {\r
51358             var o = response.responseData || Ext.decode(json);\r
51359             node.beginUpdate();\r
51360             for(var i = 0, len = o.length; i < len; i++){\r
51361                 var n = this.createNode(o[i]);\r
51362                 if(n){\r
51363                     node.appendChild(n);\r
51364                 }\r
51365             }\r
51366             node.endUpdate();\r
51367             this.runCallback(callback, scope || node, [node]);\r
51368         }catch(e){\r
51369             this.handleFailure(response);\r
51370         }\r
51371     },\r
51372 \r
51373     handleResponse : function(response){\r
51374         this.transId = false;\r
51375         var a = response.argument;\r
51376         this.processResponse(response, a.node, a.callback, a.scope);\r
51377         this.fireEvent("load", this, a.node, response);\r
51378     },\r
51379 \r
51380     handleFailure : function(response){\r
51381         this.transId = false;\r
51382         var a = response.argument;\r
51383         this.fireEvent("loadexception", this, a.node, response);\r
51384         this.runCallback(a.callback, a.scope || a.node, [a.node]);\r
51385     },\r
51386 \r
51387     destroy : function(){\r
51388         this.abort();\r
51389         this.purgeListeners();\r
51390     }\r
51391 });/**
51392  * @class Ext.tree.TreeFilter
51393  * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
51394  * @param {TreePanel} tree
51395  * @param {Object} config (optional)
51396  */
51397 Ext.tree.TreeFilter = function(tree, config){
51398     this.tree = tree;
51399     this.filtered = {};
51400     Ext.apply(this, config);
51401 };
51402
51403 Ext.tree.TreeFilter.prototype = {
51404     clearBlank:false,
51405     reverse:false,
51406     autoClear:false,
51407     remove:false,
51408
51409      /**
51410      * Filter the data by a specific attribute.
51411      * @param {String/RegExp} value Either string that the attribute value
51412      * should start with or a RegExp to test against the attribute
51413      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
51414      * @param {TreeNode} startNode (optional) The node to start the filter at.
51415      */
51416     filter : function(value, attr, startNode){
51417         attr = attr || "text";
51418         var f;
51419         if(typeof value == "string"){
51420             var vlen = value.length;
51421             // auto clear empty filter
51422             if(vlen == 0 && this.clearBlank){
51423                 this.clear();
51424                 return;
51425             }
51426             value = value.toLowerCase();
51427             f = function(n){
51428                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
51429             };
51430         }else if(value.exec){ // regex?
51431             f = function(n){
51432                 return value.test(n.attributes[attr]);
51433             };
51434         }else{
51435             throw 'Illegal filter type, must be string or regex';
51436         }
51437         this.filterBy(f, null, startNode);
51438         },
51439
51440     /**
51441      * Filter by a function. The passed function will be called with each
51442      * node in the tree (or from the startNode). If the function returns true, the node is kept
51443      * otherwise it is filtered. If a node is filtered, its children are also filtered.
51444      * @param {Function} fn The filter function
51445      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the current Node.
51446      */
51447     filterBy : function(fn, scope, startNode){
51448         startNode = startNode || this.tree.root;
51449         if(this.autoClear){
51450             this.clear();
51451         }
51452         var af = this.filtered, rv = this.reverse;
51453         var f = function(n){
51454             if(n == startNode){
51455                 return true;
51456             }
51457             if(af[n.id]){
51458                 return false;
51459             }
51460             var m = fn.call(scope || n, n);
51461             if(!m || rv){
51462                 af[n.id] = n;
51463                 n.ui.hide();
51464                 return false;
51465             }
51466             return true;
51467         };
51468         startNode.cascade(f);
51469         if(this.remove){
51470            for(var id in af){
51471                if(typeof id != "function"){
51472                    var n = af[id];
51473                    if(n && n.parentNode){
51474                        n.parentNode.removeChild(n);
51475                    }
51476                }
51477            }
51478         }
51479     },
51480
51481     /**
51482      * Clears the current filter. Note: with the "remove" option
51483      * set a filter cannot be cleared.
51484      */
51485     clear : function(){
51486         var t = this.tree;
51487         var af = this.filtered;
51488         for(var id in af){
51489             if(typeof id != "function"){
51490                 var n = af[id];
51491                 if(n){
51492                     n.ui.show();
51493                 }
51494             }
51495         }
51496         this.filtered = {};
51497     }
51498 };
51499 /**\r
51500  * @class Ext.tree.TreeSorter\r
51501  * Provides sorting of nodes in a {@link Ext.tree.TreePanel}.  The TreeSorter automatically monitors events on the\r
51502  * associated TreePanel that might affect the tree's sort order (beforechildrenrendered, append, insert and textchange).\r
51503  * Example usage:<br />\r
51504  * <pre><code>\r
51505 new Ext.tree.TreeSorter(myTree, {\r
51506     folderSort: true,\r
51507     dir: "desc",\r
51508     sortType: function(node) {\r
51509         // sort by a custom, typed attribute:\r
51510         return parseInt(node.id, 10);\r
51511     }\r
51512 });\r
51513 </code></pre>\r
51514  * @constructor\r
51515  * @param {TreePanel} tree\r
51516  * @param {Object} config\r
51517  */\r
51518 Ext.tree.TreeSorter = function(tree, config){\r
51519     /**\r
51520      * @cfg {Boolean} folderSort True to sort leaf nodes under non-leaf nodes (defaults to false)\r
51521      */\r
51522     /**\r
51523      * @cfg {String} property The named attribute on the node to sort by (defaults to "text").  Note that this\r
51524      * property is only used if no {@link #sortType} function is specified, otherwise it is ignored.\r
51525      */\r
51526     /**\r
51527      * @cfg {String} dir The direction to sort ("asc" or "desc," case-insensitive, defaults to "asc")\r
51528      */\r
51529     /**\r
51530      * @cfg {String} leafAttr The attribute used to determine leaf nodes when {@link #folderSort} = true (defaults to "leaf")\r
51531      */\r
51532     /**\r
51533      * @cfg {Boolean} caseSensitive true for case-sensitive sort (defaults to false)\r
51534      */\r
51535     /**\r
51536      * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting.  The function\r
51537      * will be called with a single parameter (the {@link Ext.tree.TreeNode} being evaluated) and is expected to return\r
51538      * the node's sort value cast to the specific data type required for sorting.  This could be used, for example, when\r
51539      * a node's text (or other attribute) should be sorted as a date or numeric value.  See the class description for\r
51540      * example usage.  Note that if a sortType is specified, any {@link #property} config will be ignored.\r
51541      */\r
51542 \r
51543     Ext.apply(this, config);\r
51544     tree.on("beforechildrenrendered", this.doSort, this);\r
51545     tree.on("append", this.updateSort, this);\r
51546     tree.on("insert", this.updateSort, this);\r
51547     tree.on("textchange", this.updateSortParent, this);\r
51548 \r
51549     var dsc = this.dir && this.dir.toLowerCase() == "desc";\r
51550     var p = this.property || "text";\r
51551     var sortType = this.sortType;\r
51552     var fs = this.folderSort;\r
51553     var cs = this.caseSensitive === true;\r
51554     var leafAttr = this.leafAttr || 'leaf';\r
51555 \r
51556     this.sortFn = function(n1, n2){\r
51557         if(fs){\r
51558             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){\r
51559                 return 1;\r
51560             }\r
51561             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){\r
51562                 return -1;\r
51563             }\r
51564         }\r
51565         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());\r
51566         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());\r
51567         if(v1 < v2){\r
51568             return dsc ? +1 : -1;\r
51569         }else if(v1 > v2){\r
51570             return dsc ? -1 : +1;\r
51571         }else{\r
51572             return 0;\r
51573         }\r
51574     };\r
51575 };\r
51576 \r
51577 Ext.tree.TreeSorter.prototype = {\r
51578     doSort : function(node){\r
51579         node.sort(this.sortFn);\r
51580     },\r
51581 \r
51582     compareNodes : function(n1, n2){\r
51583         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);\r
51584     },\r
51585 \r
51586     updateSort : function(tree, node){\r
51587         if(node.childrenRendered){\r
51588             this.doSort.defer(1, this, [node]);\r
51589         }\r
51590     },\r
51591 \r
51592     updateSortParent : function(node){\r
51593         var p = node.parentNode;\r
51594         if(p && p.childrenRendered){\r
51595             this.doSort.defer(1, this, [p]);\r
51596         }\r
51597     }\r
51598 };/**\r
51599  * @class Ext.tree.TreeDropZone\r
51600  * @extends Ext.dd.DropZone\r
51601  * @constructor\r
51602  * @param {String/HTMLElement/Element} tree The {@link Ext.tree.TreePanel} for which to enable dropping\r
51603  * @param {Object} config\r
51604  */\r
51605 if(Ext.dd.DropZone){\r
51606     \r
51607 Ext.tree.TreeDropZone = function(tree, config){\r
51608     /**\r
51609      * @cfg {Boolean} allowParentInsert\r
51610      * Allow inserting a dragged node between an expanded parent node and its first child that will become a\r
51611      * sibling of the parent when dropped (defaults to false)\r
51612      */\r
51613     this.allowParentInsert = config.allowParentInsert || false;\r
51614     /**\r
51615      * @cfg {String} allowContainerDrop\r
51616      * True if drops on the tree container (outside of a specific tree node) are allowed (defaults to false)\r
51617      */\r
51618     this.allowContainerDrop = config.allowContainerDrop || false;\r
51619     /**\r
51620      * @cfg {String} appendOnly\r
51621      * True if the tree should only allow append drops (use for trees which are sorted, defaults to false)\r
51622      */\r
51623     this.appendOnly = config.appendOnly || false;\r
51624 \r
51625     Ext.tree.TreeDropZone.superclass.constructor.call(this, tree.getTreeEl(), config);\r
51626     /**\r
51627     * The TreePanel for this drop zone\r
51628     * @type Ext.tree.TreePanel\r
51629     * @property\r
51630     */\r
51631     this.tree = tree;\r
51632     /**\r
51633     * Arbitrary data that can be associated with this tree and will be included in the event object that gets\r
51634     * passed to any nodedragover event handler (defaults to {})\r
51635     * @type Ext.tree.TreePanel\r
51636     * @property\r
51637     */\r
51638     this.dragOverData = {};\r
51639     // private\r
51640     this.lastInsertClass = "x-tree-no-status";\r
51641 };\r
51642 \r
51643 Ext.extend(Ext.tree.TreeDropZone, Ext.dd.DropZone, {\r
51644     /**\r
51645      * @cfg {String} ddGroup\r
51646      * A named drag drop group to which this object belongs.  If a group is specified, then this object will only\r
51647      * interact with other drag drop objects in the same group (defaults to 'TreeDD').\r
51648      */\r
51649     ddGroup : "TreeDD",\r
51650 \r
51651     /**\r
51652      * @cfg {String} expandDelay\r
51653      * The delay in milliseconds to wait before expanding a target tree node while dragging a droppable node\r
51654      * over the target (defaults to 1000)\r
51655      */\r
51656     expandDelay : 1000,\r
51657 \r
51658     // private\r
51659     expandNode : function(node){\r
51660         if(node.hasChildNodes() && !node.isExpanded()){\r
51661             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));\r
51662         }\r
51663     },\r
51664 \r
51665     // private\r
51666     queueExpand : function(node){\r
51667         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);\r
51668     },\r
51669 \r
51670     // private\r
51671     cancelExpand : function(){\r
51672         if(this.expandProcId){\r
51673             clearTimeout(this.expandProcId);\r
51674             this.expandProcId = false;\r
51675         }\r
51676     },\r
51677 \r
51678     // private\r
51679     isValidDropPoint : function(n, pt, dd, e, data){\r
51680         if(!n || !data){ return false; }\r
51681         var targetNode = n.node;\r
51682         var dropNode = data.node;\r
51683         // default drop rules\r
51684         if(!(targetNode && targetNode.isTarget && pt)){\r
51685             return false;\r
51686         }\r
51687         if(pt == "append" && targetNode.allowChildren === false){\r
51688             return false;\r
51689         }\r
51690         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){\r
51691             return false;\r
51692         }\r
51693         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){\r
51694             return false;\r
51695         }\r
51696         // reuse the object\r
51697         var overEvent = this.dragOverData;\r
51698         overEvent.tree = this.tree;\r
51699         overEvent.target = targetNode;\r
51700         overEvent.data = data;\r
51701         overEvent.point = pt;\r
51702         overEvent.source = dd;\r
51703         overEvent.rawEvent = e;\r
51704         overEvent.dropNode = dropNode;\r
51705         overEvent.cancel = false;  \r
51706         var result = this.tree.fireEvent("nodedragover", overEvent);\r
51707         return overEvent.cancel === false && result !== false;\r
51708     },\r
51709 \r
51710     // private\r
51711     getDropPoint : function(e, n, dd){\r
51712         var tn = n.node;\r
51713         if(tn.isRoot){\r
51714             return tn.allowChildren !== false ? "append" : false; // always append for root\r
51715         }\r
51716         var dragEl = n.ddel;\r
51717         var t = Ext.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;\r
51718         var y = Ext.lib.Event.getPageY(e);\r
51719         var noAppend = tn.allowChildren === false || tn.isLeaf();\r
51720         if(this.appendOnly || tn.parentNode.allowChildren === false){\r
51721             return noAppend ? false : "append";\r
51722         }\r
51723         var noBelow = false;\r
51724         if(!this.allowParentInsert){\r
51725             noBelow = tn.hasChildNodes() && tn.isExpanded();\r
51726         }\r
51727         var q = (b - t) / (noAppend ? 2 : 3);\r
51728         if(y >= t && y < (t + q)){\r
51729             return "above";\r
51730         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){\r
51731             return "below";\r
51732         }else{\r
51733             return "append";\r
51734         }\r
51735     },\r
51736 \r
51737     // private\r
51738     onNodeEnter : function(n, dd, e, data){\r
51739         this.cancelExpand();\r
51740     },\r
51741     \r
51742     onContainerOver : function(dd, e, data) {\r
51743         if (this.allowContainerDrop && this.isValidDropPoint({ ddel: this.tree.getRootNode().ui.elNode, node: this.tree.getRootNode() }, "append", dd, e, data)) {\r
51744             return this.dropAllowed;\r
51745         }\r
51746         return this.dropNotAllowed;\r
51747     },\r
51748 \r
51749     // private\r
51750     onNodeOver : function(n, dd, e, data){\r
51751         var pt = this.getDropPoint(e, n, dd);\r
51752         var node = n.node;\r
51753         \r
51754         // auto node expand check\r
51755         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){\r
51756             this.queueExpand(node);\r
51757         }else if(pt != "append"){\r
51758             this.cancelExpand();\r
51759         }\r
51760         \r
51761         // set the insert point style on the target node\r
51762         var returnCls = this.dropNotAllowed;\r
51763         if(this.isValidDropPoint(n, pt, dd, e, data)){\r
51764            if(pt){\r
51765                var el = n.ddel;\r
51766                var cls;\r
51767                if(pt == "above"){\r
51768                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";\r
51769                    cls = "x-tree-drag-insert-above";\r
51770                }else if(pt == "below"){\r
51771                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";\r
51772                    cls = "x-tree-drag-insert-below";\r
51773                }else{\r
51774                    returnCls = "x-tree-drop-ok-append";\r
51775                    cls = "x-tree-drag-append";\r
51776                }\r
51777                if(this.lastInsertClass != cls){\r
51778                    Ext.fly(el).replaceClass(this.lastInsertClass, cls);\r
51779                    this.lastInsertClass = cls;\r
51780                }\r
51781            }\r
51782        }\r
51783        return returnCls;\r
51784     },\r
51785 \r
51786     // private\r
51787     onNodeOut : function(n, dd, e, data){\r
51788         this.cancelExpand();\r
51789         this.removeDropIndicators(n);\r
51790     },\r
51791 \r
51792     // private\r
51793     onNodeDrop : function(n, dd, e, data){\r
51794         var point = this.getDropPoint(e, n, dd);\r
51795         var targetNode = n.node;\r
51796         targetNode.ui.startDrop();\r
51797         if(!this.isValidDropPoint(n, point, dd, e, data)){\r
51798             targetNode.ui.endDrop();\r
51799             return false;\r
51800         }\r
51801         // first try to find the drop node\r
51802         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);\r
51803         return this.processDrop(targetNode, data, point, dd, e, dropNode);\r
51804     },\r
51805     \r
51806     onContainerDrop : function(dd, e, data){\r
51807         if (this.allowContainerDrop && this.isValidDropPoint({ ddel: this.tree.getRootNode().ui.elNode, node: this.tree.getRootNode() }, "append", dd, e, data)) {\r
51808             var targetNode = this.tree.getRootNode();       \r
51809             targetNode.ui.startDrop();\r
51810             var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, 'append', e) : null);\r
51811             return this.processDrop(targetNode, data, 'append', dd, e, dropNode);\r
51812         }\r
51813         return false;\r
51814     },\r
51815     \r
51816     // private\r
51817     processDrop: function(target, data, point, dd, e, dropNode){\r
51818         var dropEvent = {\r
51819             tree : this.tree,\r
51820             target: target,\r
51821             data: data,\r
51822             point: point,\r
51823             source: dd,\r
51824             rawEvent: e,\r
51825             dropNode: dropNode,\r
51826             cancel: !dropNode,\r
51827             dropStatus: false\r
51828         };\r
51829         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);\r
51830         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){\r
51831             target.ui.endDrop();\r
51832             return dropEvent.dropStatus;\r
51833         }\r
51834     \r
51835         target = dropEvent.target;\r
51836         if(point == 'append' && !target.isExpanded()){\r
51837             target.expand(false, null, function(){\r
51838                 this.completeDrop(dropEvent);\r
51839             }.createDelegate(this));\r
51840         }else{\r
51841             this.completeDrop(dropEvent);\r
51842         }\r
51843         return true;\r
51844     },\r
51845 \r
51846     // private\r
51847     completeDrop : function(de){\r
51848         var ns = de.dropNode, p = de.point, t = de.target;\r
51849         if(!Ext.isArray(ns)){\r
51850             ns = [ns];\r
51851         }\r
51852         var n;\r
51853         for(var i = 0, len = ns.length; i < len; i++){\r
51854             n = ns[i];\r
51855             if(p == "above"){\r
51856                 t.parentNode.insertBefore(n, t);\r
51857             }else if(p == "below"){\r
51858                 t.parentNode.insertBefore(n, t.nextSibling);\r
51859             }else{\r
51860                 t.appendChild(n);\r
51861             }\r
51862         }\r
51863         n.ui.focus();\r
51864         if(Ext.enableFx && this.tree.hlDrop){\r
51865             n.ui.highlight();\r
51866         }\r
51867         t.ui.endDrop();\r
51868         this.tree.fireEvent("nodedrop", de);\r
51869     },\r
51870 \r
51871     // private\r
51872     afterNodeMoved : function(dd, data, e, targetNode, dropNode){\r
51873         if(Ext.enableFx && this.tree.hlDrop){\r
51874             dropNode.ui.focus();\r
51875             dropNode.ui.highlight();\r
51876         }\r
51877         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);\r
51878     },\r
51879 \r
51880     // private\r
51881     getTree : function(){\r
51882         return this.tree;\r
51883     },\r
51884 \r
51885     // private\r
51886     removeDropIndicators : function(n){\r
51887         if(n && n.ddel){\r
51888             var el = n.ddel;\r
51889             Ext.fly(el).removeClass([\r
51890                     "x-tree-drag-insert-above",\r
51891                     "x-tree-drag-insert-below",\r
51892                     "x-tree-drag-append"]);\r
51893             this.lastInsertClass = "_noclass";\r
51894         }\r
51895     },\r
51896 \r
51897     // private\r
51898     beforeDragDrop : function(target, e, id){\r
51899         this.cancelExpand();\r
51900         return true;\r
51901     },\r
51902 \r
51903     // private\r
51904     afterRepair : function(data){\r
51905         if(data && Ext.enableFx){\r
51906             data.node.ui.highlight();\r
51907         }\r
51908         this.hideProxy();\r
51909     }    \r
51910 });\r
51911 \r
51912 }/**\r
51913  * @class Ext.tree.TreeDragZone\r
51914  * @extends Ext.dd.DragZone\r
51915  * @constructor\r
51916  * @param {String/HTMLElement/Element} tree The {@link Ext.tree.TreePanel} for which to enable dragging\r
51917  * @param {Object} config\r
51918  */\r
51919 if(Ext.dd.DragZone){\r
51920 Ext.tree.TreeDragZone = function(tree, config){\r
51921     Ext.tree.TreeDragZone.superclass.constructor.call(this, tree.innerCt, config);\r
51922     /**\r
51923     * The TreePanel for this drag zone\r
51924     * @type Ext.tree.TreePanel\r
51925     * @property\r
51926     */\r
51927     this.tree = tree;\r
51928 };\r
51929 \r
51930 Ext.extend(Ext.tree.TreeDragZone, Ext.dd.DragZone, {\r
51931     /**\r
51932      * @cfg {String} ddGroup\r
51933      * A named drag drop group to which this object belongs.  If a group is specified, then this object will only\r
51934      * interact with other drag drop objects in the same group (defaults to 'TreeDD').\r
51935      */\r
51936     ddGroup : "TreeDD",\r
51937 \r
51938     // private\r
51939     onBeforeDrag : function(data, e){\r
51940         var n = data.node;\r
51941         return n && n.draggable && !n.disabled;\r
51942     },\r
51943 \r
51944     // private\r
51945     onInitDrag : function(e){\r
51946         var data = this.dragData;\r
51947         this.tree.getSelectionModel().select(data.node);\r
51948         this.tree.eventModel.disable();\r
51949         this.proxy.update("");\r
51950         data.node.ui.appendDDGhost(this.proxy.ghost.dom);\r
51951         this.tree.fireEvent("startdrag", this.tree, data.node, e);\r
51952     },\r
51953 \r
51954     // private\r
51955     getRepairXY : function(e, data){\r
51956         return data.node.ui.getDDRepairXY();\r
51957     },\r
51958 \r
51959     // private\r
51960     onEndDrag : function(data, e){\r
51961         this.tree.eventModel.enable.defer(100, this.tree.eventModel);\r
51962         this.tree.fireEvent("enddrag", this.tree, data.node, e);\r
51963     },\r
51964 \r
51965     // private\r
51966     onValidDrop : function(dd, e, id){\r
51967         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);\r
51968         this.hideProxy();\r
51969     },\r
51970 \r
51971     // private\r
51972     beforeInvalidDrop : function(e, id){\r
51973         // this scrolls the original position back into view\r
51974         var sm = this.tree.getSelectionModel();\r
51975         sm.clearSelections();\r
51976         sm.select(this.dragData.node);\r
51977     },\r
51978     \r
51979     // private\r
51980     afterRepair : function(){\r
51981         if (Ext.enableFx && this.tree.hlDrop) {\r
51982             Ext.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");\r
51983         }\r
51984         this.dragging = false;\r
51985     }\r
51986 });\r
51987 }/**
51988  * @class Ext.tree.TreeEditor
51989  * @extends Ext.Editor
51990  * Provides editor functionality for inline tree node editing.  Any valid {@link Ext.form.Field} subclass can be used
51991  * as the editor field.
51992  * @constructor
51993  * @param {TreePanel} tree
51994  * @param {Object} fieldConfig (optional) Either a prebuilt {@link Ext.form.Field} instance or a Field config object
51995  * that will be applied to the default field instance (defaults to a {@link Ext.form.TextField}).
51996  * @param {Object} config (optional) A TreeEditor config object
51997  */
51998 Ext.tree.TreeEditor = function(tree, fc, config){
51999     fc = fc || {};
52000     var field = fc.events ? fc : new Ext.form.TextField(fc);
52001     
52002     Ext.tree.TreeEditor.superclass.constructor.call(this, field, config);
52003
52004     this.tree = tree;
52005
52006     if(!tree.rendered){
52007         tree.on('render', this.initEditor, this);
52008     }else{
52009         this.initEditor(tree);
52010     }
52011 };
52012
52013 Ext.extend(Ext.tree.TreeEditor, Ext.Editor, {
52014     /**
52015      * @cfg {String} alignment
52016      * The position to align to (see {@link Ext.Element#alignTo} for more details, defaults to "l-l").
52017      */
52018     alignment: "l-l",
52019     // inherit
52020     autoSize: false,
52021     /**
52022      * @cfg {Boolean} hideEl
52023      * True to hide the bound element while the editor is displayed (defaults to false)
52024      */
52025     hideEl : false,
52026     /**
52027      * @cfg {String} cls
52028      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
52029      */
52030     cls: "x-small-editor x-tree-editor",
52031     /**
52032      * @cfg {Boolean} shim
52033      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
52034      */
52035     shim:false,
52036     // inherit
52037     shadow:"frame",
52038     /**
52039      * @cfg {Number} maxWidth
52040      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
52041      * the containing tree element's size, it will be automatically limited for you to the container width, taking
52042      * scroll and client offsets into account prior to each edit.
52043      */
52044     maxWidth: 250,
52045     /**
52046      * @cfg {Number} editDelay The number of milliseconds between clicks to register a double-click that will trigger
52047      * editing on the current node (defaults to 350).  If two clicks occur on the same node within this time span,
52048      * the editor for the node will display, otherwise it will be processed as a regular click.
52049      */
52050     editDelay : 350,
52051
52052     initEditor : function(tree){
52053         tree.on({
52054             scope      : this,
52055             beforeclick: this.beforeNodeClick,
52056             dblclick   : this.onNodeDblClick
52057         });
52058         
52059         this.on({
52060             scope          : this,
52061             complete       : this.updateNode,
52062             beforestartedit: this.fitToTree,
52063             specialkey     : this.onSpecialKey
52064         });
52065         
52066         this.on('startedit', this.bindScroll, this, {delay:10});
52067     },
52068
52069     // private
52070     fitToTree : function(ed, el){
52071         var td = this.tree.getTreeEl().dom, nd = el.dom;
52072         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
52073             td.scrollLeft = nd.offsetLeft;
52074         }
52075         var w = Math.min(
52076                 this.maxWidth,
52077                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
52078         this.setSize(w, '');
52079     },
52080
52081     /**
52082      * Edit the text of the passed {@link Ext.tree.TreeNode TreeNode}.
52083      * @param node {Ext.tree.TreeNode} The TreeNode to edit. The TreeNode must be {@link Ext.tree.TreeNode#editable editable}.
52084      */
52085     triggerEdit : function(node, defer){
52086         this.completeEdit();
52087                 if(node.attributes.editable !== false){
52088            /**
52089             * The {@link Ext.tree.TreeNode TreeNode} this editor is bound to. Read-only.
52090             * @type Ext.tree.TreeNode
52091             * @property editNode
52092             */
52093                         this.editNode = node;
52094             if(this.tree.autoScroll){
52095                 Ext.fly(node.ui.getEl()).scrollIntoView(this.tree.body);
52096             }
52097             var value = node.text || '';
52098             if (!Ext.isGecko && Ext.isEmpty(node.text)){
52099                 node.setText('&#160;');
52100             }
52101             this.autoEditTimer = this.startEdit.defer(this.editDelay, this, [node.ui.textNode, value]);
52102             return false;
52103         }
52104     },
52105
52106     // private
52107     bindScroll : function(){
52108         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
52109     },
52110
52111     // private
52112     beforeNodeClick : function(node, e){
52113         clearTimeout(this.autoEditTimer);
52114         if(this.tree.getSelectionModel().isSelected(node)){
52115             e.stopEvent();
52116             return this.triggerEdit(node);
52117         }
52118     },
52119
52120     onNodeDblClick : function(node, e){
52121         clearTimeout(this.autoEditTimer);
52122     },
52123
52124     // private
52125     updateNode : function(ed, value){
52126         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
52127         this.editNode.setText(value);
52128     },
52129
52130     // private
52131     onHide : function(){
52132         Ext.tree.TreeEditor.superclass.onHide.call(this);
52133         if(this.editNode){
52134             this.editNode.ui.focus.defer(50, this.editNode.ui);
52135         }
52136     },
52137
52138     // private
52139     onSpecialKey : function(field, e){
52140         var k = e.getKey();
52141         if(k == e.ESC){
52142             e.stopEvent();
52143             this.cancelEdit();
52144         }else if(k == e.ENTER && !e.hasModifier()){
52145             e.stopEvent();
52146             this.completeEdit();
52147         }
52148     },
52149     
52150     onDestroy : function(){
52151         clearTimeout(this.autoEditTimer);
52152         Ext.tree.TreeEditor.superclass.onDestroy.call(this);
52153         var tree = this.tree;
52154         tree.un('beforeclick', this.beforeNodeClick, this);
52155         tree.un('dblclick', this.onNodeDblClick, this);
52156     }
52157 });/*! SWFObject v2.2 <http://code.google.com/p/swfobject/> \r
52158     is released under the MIT License <http://www.opensource.org/licenses/mit-license.php> \r
52159 */\r
52160 \r
52161 var swfobject = function() {\r
52162     \r
52163     var UNDEF = "undefined",\r
52164         OBJECT = "object",\r
52165         SHOCKWAVE_FLASH = "Shockwave Flash",\r
52166         SHOCKWAVE_FLASH_AX = "ShockwaveFlash.ShockwaveFlash",\r
52167         FLASH_MIME_TYPE = "application/x-shockwave-flash",\r
52168         EXPRESS_INSTALL_ID = "SWFObjectExprInst",\r
52169         ON_READY_STATE_CHANGE = "onreadystatechange",\r
52170         \r
52171         win = window,\r
52172         doc = document,\r
52173         nav = navigator,\r
52174         \r
52175         plugin = false,\r
52176         domLoadFnArr = [main],\r
52177         regObjArr = [],\r
52178         objIdArr = [],\r
52179         listenersArr = [],\r
52180         storedAltContent,\r
52181         storedAltContentId,\r
52182         storedCallbackFn,\r
52183         storedCallbackObj,\r
52184         isDomLoaded = false,\r
52185         isExpressInstallActive = false,\r
52186         dynamicStylesheet,\r
52187         dynamicStylesheetMedia,\r
52188         autoHideShow = true,\r
52189     \r
52190     /* Centralized function for browser feature detection\r
52191         - User agent string detection is only used when no good alternative is possible\r
52192         - Is executed directly for optimal performance\r
52193     */  \r
52194     ua = function() {\r
52195         var w3cdom = typeof doc.getElementById != UNDEF && typeof doc.getElementsByTagName != UNDEF && typeof doc.createElement != UNDEF,\r
52196             u = nav.userAgent.toLowerCase(),\r
52197             p = nav.platform.toLowerCase(),\r
52198             windows = p ? /win/.test(p) : /win/.test(u),\r
52199             mac = p ? /mac/.test(p) : /mac/.test(u),\r
52200             webkit = /webkit/.test(u) ? parseFloat(u.replace(/^.*webkit\/(\d+(\.\d+)?).*$/, "$1")) : false, // returns either the webkit version or false if not webkit\r
52201             ie = !+"\v1", // feature detection based on Andrea Giammarchi's solution: http://webreflection.blogspot.com/2009/01/32-bytes-to-know-if-your-browser-is-ie.html\r
52202             playerVersion = [0,0,0],\r
52203             d = null;\r
52204         if (typeof nav.plugins != UNDEF && typeof nav.plugins[SHOCKWAVE_FLASH] == OBJECT) {\r
52205             d = nav.plugins[SHOCKWAVE_FLASH].description;\r
52206             if (d && !(typeof nav.mimeTypes != UNDEF && nav.mimeTypes[FLASH_MIME_TYPE] && !nav.mimeTypes[FLASH_MIME_TYPE].enabledPlugin)) { // navigator.mimeTypes["application/x-shockwave-flash"].enabledPlugin indicates whether plug-ins are enabled or disabled in Safari 3+\r
52207                 plugin = true;\r
52208                 ie = false; // cascaded feature detection for Internet Explorer\r
52209                 d = d.replace(/^.*\s+(\S+\s+\S+$)/, "$1");\r
52210                 playerVersion[0] = parseInt(d.replace(/^(.*)\..*$/, "$1"), 10);\r
52211                 playerVersion[1] = parseInt(d.replace(/^.*\.(.*)\s.*$/, "$1"), 10);\r
52212                 playerVersion[2] = /[a-zA-Z]/.test(d) ? parseInt(d.replace(/^.*[a-zA-Z]+(.*)$/, "$1"), 10) : 0;\r
52213             }\r
52214         }\r
52215         else if (typeof win.ActiveXObject != UNDEF) {\r
52216             try {\r
52217                 var a = new ActiveXObject(SHOCKWAVE_FLASH_AX);\r
52218                 if (a) { // a will return null when ActiveX is disabled\r
52219                     d = a.GetVariable("$version");\r
52220                     if (d) {\r
52221                         ie = true; // cascaded feature detection for Internet Explorer\r
52222                         d = d.split(" ")[1].split(",");\r
52223                         playerVersion = [parseInt(d[0], 10), parseInt(d[1], 10), parseInt(d[2], 10)];\r
52224                     }\r
52225                 }\r
52226             }\r
52227             catch(e) {}\r
52228         }\r
52229         return { w3:w3cdom, pv:playerVersion, wk:webkit, ie:ie, win:windows, mac:mac };\r
52230     }(),\r
52231     \r
52232     /* Cross-browser onDomLoad\r
52233         - Will fire an event as soon as the DOM of a web page is loaded\r
52234         - Internet Explorer workaround based on Diego Perini's solution: http://javascript.nwbox.com/IEContentLoaded/\r
52235         - Regular onload serves as fallback\r
52236     */ \r
52237     onDomLoad = function() {\r
52238         if (!ua.w3) { return; }\r
52239         if ((typeof doc.readyState != UNDEF && doc.readyState == "complete") || (typeof doc.readyState == UNDEF && (doc.getElementsByTagName("body")[0] || doc.body))) { // function is fired after onload, e.g. when script is inserted dynamically \r
52240             callDomLoadFunctions();\r
52241         }\r
52242         if (!isDomLoaded) {\r
52243             if (typeof doc.addEventListener != UNDEF) {\r
52244                 doc.addEventListener("DOMContentLoaded", callDomLoadFunctions, false);\r
52245             }       \r
52246             if (ua.ie && ua.win) {\r
52247                 doc.attachEvent(ON_READY_STATE_CHANGE, function() {\r
52248                     if (doc.readyState == "complete") {\r
52249                         doc.detachEvent(ON_READY_STATE_CHANGE, arguments.callee);\r
52250                         callDomLoadFunctions();\r
52251                     }\r
52252                 });\r
52253                 if (win == top) { // if not inside an iframe\r
52254                     (function(){\r
52255                         if (isDomLoaded) { return; }\r
52256                         try {\r
52257                             doc.documentElement.doScroll("left");\r
52258                         }\r
52259                         catch(e) {\r
52260                             setTimeout(arguments.callee, 0);\r
52261                             return;\r
52262                         }\r
52263                         callDomLoadFunctions();\r
52264                     })();\r
52265                 }\r
52266             }\r
52267             if (ua.wk) {\r
52268                 (function(){\r
52269                     if (isDomLoaded) { return; }\r
52270                     if (!/loaded|complete/.test(doc.readyState)) {\r
52271                         setTimeout(arguments.callee, 0);\r
52272                         return;\r
52273                     }\r
52274                     callDomLoadFunctions();\r
52275                 })();\r
52276             }\r
52277             addLoadEvent(callDomLoadFunctions);\r
52278         }\r
52279     }();\r
52280     \r
52281     function callDomLoadFunctions() {\r
52282         if (isDomLoaded) { return; }\r
52283         try { // test if we can really add/remove elements to/from the DOM; we don't want to fire it too early\r
52284             var t = doc.getElementsByTagName("body")[0].appendChild(createElement("span"));\r
52285             t.parentNode.removeChild(t);\r
52286         }\r
52287         catch (e) { return; }\r
52288         isDomLoaded = true;\r
52289         var dl = domLoadFnArr.length;\r
52290         for (var i = 0; i < dl; i++) {\r
52291             domLoadFnArr[i]();\r
52292         }\r
52293     }\r
52294     \r
52295     function addDomLoadEvent(fn) {\r
52296         if (isDomLoaded) {\r
52297             fn();\r
52298         }\r
52299         else { \r
52300             domLoadFnArr[domLoadFnArr.length] = fn; // Array.push() is only available in IE5.5+\r
52301         }\r
52302     }\r
52303     \r
52304     /* Cross-browser onload\r
52305         - Based on James Edwards' solution: http://brothercake.com/site/resources/scripts/onload/\r
52306         - Will fire an event as soon as a web page including all of its assets are loaded \r
52307      */\r
52308     function addLoadEvent(fn) {\r
52309         if (typeof win.addEventListener != UNDEF) {\r
52310             win.addEventListener("load", fn, false);\r
52311         }\r
52312         else if (typeof doc.addEventListener != UNDEF) {\r
52313             doc.addEventListener("load", fn, false);\r
52314         }\r
52315         else if (typeof win.attachEvent != UNDEF) {\r
52316             addListener(win, "onload", fn);\r
52317         }\r
52318         else if (typeof win.onload == "function") {\r
52319             var fnOld = win.onload;\r
52320             win.onload = function() {\r
52321                 fnOld();\r
52322                 fn();\r
52323             };\r
52324         }\r
52325         else {\r
52326             win.onload = fn;\r
52327         }\r
52328     }\r
52329     \r
52330     /* Main function\r
52331         - Will preferably execute onDomLoad, otherwise onload (as a fallback)\r
52332     */\r
52333     function main() { \r
52334         if (plugin) {\r
52335             testPlayerVersion();\r
52336         }\r
52337         else {\r
52338             matchVersions();\r
52339         }\r
52340     }\r
52341     \r
52342     /* Detect the Flash Player version for non-Internet Explorer browsers\r
52343         - Detecting the plug-in version via the object element is more precise than using the plugins collection item's description:\r
52344           a. Both release and build numbers can be detected\r
52345           b. Avoid wrong descriptions by corrupt installers provided by Adobe\r
52346           c. Avoid wrong descriptions by multiple Flash Player entries in the plugin Array, caused by incorrect browser imports\r
52347         - Disadvantage of this method is that it depends on the availability of the DOM, while the plugins collection is immediately available\r
52348     */\r
52349     function testPlayerVersion() {\r
52350         var b = doc.getElementsByTagName("body")[0];\r
52351         var o = createElement(OBJECT);\r
52352         o.setAttribute("type", FLASH_MIME_TYPE);\r
52353         var t = b.appendChild(o);\r
52354         if (t) {\r
52355             var counter = 0;\r
52356             (function(){\r
52357                 if (typeof t.GetVariable != UNDEF) {\r
52358                     var d = t.GetVariable("$version");\r
52359                     if (d) {\r
52360                         d = d.split(" ")[1].split(",");\r
52361                         ua.pv = [parseInt(d[0], 10), parseInt(d[1], 10), parseInt(d[2], 10)];\r
52362                     }\r
52363                 }\r
52364                 else if (counter < 10) {\r
52365                     counter++;\r
52366                     setTimeout(arguments.callee, 10);\r
52367                     return;\r
52368                 }\r
52369                 b.removeChild(o);\r
52370                 t = null;\r
52371                 matchVersions();\r
52372             })();\r
52373         }\r
52374         else {\r
52375             matchVersions();\r
52376         }\r
52377     }\r
52378     \r
52379     /* Perform Flash Player and SWF version matching; static publishing only\r
52380     */\r
52381     function matchVersions() {\r
52382         var rl = regObjArr.length;\r
52383         if (rl > 0) {\r
52384             for (var i = 0; i < rl; i++) { // for each registered object element\r
52385                 var id = regObjArr[i].id;\r
52386                 var cb = regObjArr[i].callbackFn;\r
52387                 var cbObj = {success:false, id:id};\r
52388                 if (ua.pv[0] > 0) {\r
52389                     var obj = getElementById(id);\r
52390                     if (obj) {\r
52391                         if (hasPlayerVersion(regObjArr[i].swfVersion) && !(ua.wk && ua.wk < 312)) { // Flash Player version >= published SWF version: Houston, we have a match!\r
52392                             setVisibility(id, true);\r
52393                             if (cb) {\r
52394                                 cbObj.success = true;\r
52395                                 cbObj.ref = getObjectById(id);\r
52396                                 cb(cbObj);\r
52397                             }\r
52398                         }\r
52399                         else if (regObjArr[i].expressInstall && canExpressInstall()) { // show the Adobe Express Install dialog if set by the web page author and if supported\r
52400                             var att = {};\r
52401                             att.data = regObjArr[i].expressInstall;\r
52402                             att.width = obj.getAttribute("width") || "0";\r
52403                             att.height = obj.getAttribute("height") || "0";\r
52404                             if (obj.getAttribute("class")) { att.styleclass = obj.getAttribute("class"); }\r
52405                             if (obj.getAttribute("align")) { att.align = obj.getAttribute("align"); }\r
52406                             // parse HTML object param element's name-value pairs\r
52407                             var par = {};\r
52408                             var p = obj.getElementsByTagName("param");\r
52409                             var pl = p.length;\r
52410                             for (var j = 0; j < pl; j++) {\r
52411                                 if (p[j].getAttribute("name").toLowerCase() != "movie") {\r
52412                                     par[p[j].getAttribute("name")] = p[j].getAttribute("value");\r
52413                                 }\r
52414                             }\r
52415                             showExpressInstall(att, par, id, cb);\r
52416                         }\r
52417                         else { // Flash Player and SWF version mismatch or an older Webkit engine that ignores the HTML object element's nested param elements: display alternative content instead of SWF\r
52418                             displayAltContent(obj);\r
52419                             if (cb) { cb(cbObj); }\r
52420                         }\r
52421                     }\r
52422                 }\r
52423                 else {  // if no Flash Player is installed or the fp version cannot be detected we let the HTML object element do its job (either show a SWF or alternative content)\r
52424                     setVisibility(id, true);\r
52425                     if (cb) {\r
52426                         var o = getObjectById(id); // test whether there is an HTML object element or not\r
52427                         if (o && typeof o.SetVariable != UNDEF) { \r
52428                             cbObj.success = true;\r
52429                             cbObj.ref = o;\r
52430                         }\r
52431                         cb(cbObj);\r
52432                     }\r
52433                 }\r
52434             }\r
52435         }\r
52436     }\r
52437     \r
52438     function getObjectById(objectIdStr) {\r
52439         var r = null;\r
52440         var o = getElementById(objectIdStr);\r
52441         if (o && o.nodeName == "OBJECT") {\r
52442             if (typeof o.SetVariable != UNDEF) {\r
52443                 r = o;\r
52444             }\r
52445             else {\r
52446                 var n = o.getElementsByTagName(OBJECT)[0];\r
52447                 if (n) {\r
52448                     r = n;\r
52449                 }\r
52450             }\r
52451         }\r
52452         return r;\r
52453     }\r
52454     \r
52455     /* Requirements for Adobe Express Install\r
52456         - only one instance can be active at a time\r
52457         - fp 6.0.65 or higher\r
52458         - Win/Mac OS only\r
52459         - no Webkit engines older than version 312\r
52460     */\r
52461     function canExpressInstall() {\r
52462         return !isExpressInstallActive && hasPlayerVersion("6.0.65") && (ua.win || ua.mac) && !(ua.wk && ua.wk < 312);\r
52463     }\r
52464     \r
52465     /* Show the Adobe Express Install dialog\r
52466         - Reference: http://www.adobe.com/cfusion/knowledgebase/index.cfm?id=6a253b75\r
52467     */\r
52468     function showExpressInstall(att, par, replaceElemIdStr, callbackFn) {\r
52469         isExpressInstallActive = true;\r
52470         storedCallbackFn = callbackFn || null;\r
52471         storedCallbackObj = {success:false, id:replaceElemIdStr};\r
52472         var obj = getElementById(replaceElemIdStr);\r
52473         if (obj) {\r
52474             if (obj.nodeName == "OBJECT") { // static publishing\r
52475                 storedAltContent = abstractAltContent(obj);\r
52476                 storedAltContentId = null;\r
52477             }\r
52478             else { // dynamic publishing\r
52479                 storedAltContent = obj;\r
52480                 storedAltContentId = replaceElemIdStr;\r
52481             }\r
52482             att.id = EXPRESS_INSTALL_ID;\r
52483             if (typeof att.width == UNDEF || (!/%$/.test(att.width) && parseInt(att.width, 10) < 310)) { att.width = "310"; }\r
52484             if (typeof att.height == UNDEF || (!/%$/.test(att.height) && parseInt(att.height, 10) < 137)) { att.height = "137"; }\r
52485             doc.title = doc.title.slice(0, 47) + " - Flash Player Installation";\r
52486             var pt = ua.ie && ua.win ? "ActiveX" : "PlugIn",\r
52487                 fv = "MMredirectURL=" + win.location.toString().replace(/&/g,"%26") + "&MMplayerType=" + pt + "&MMdoctitle=" + doc.title;\r
52488             if (typeof par.flashvars != UNDEF) {\r
52489                 par.flashvars += "&" + fv;\r
52490             }\r
52491             else {\r
52492                 par.flashvars = fv;\r
52493             }\r
52494             // IE only: when a SWF is loading (AND: not available in cache) wait for the readyState of the object element to become 4 before removing it,\r
52495             // because you cannot properly cancel a loading SWF file without breaking browser load references, also obj.onreadystatechange doesn't work\r
52496             if (ua.ie && ua.win && obj.readyState != 4) {\r
52497                 var newObj = createElement("div");\r
52498                 replaceElemIdStr += "SWFObjectNew";\r
52499                 newObj.setAttribute("id", replaceElemIdStr);\r
52500                 obj.parentNode.insertBefore(newObj, obj); // insert placeholder div that will be replaced by the object element that loads expressinstall.swf\r
52501                 obj.style.display = "none";\r
52502                 (function(){\r
52503                     if (obj.readyState == 4) {\r
52504                         obj.parentNode.removeChild(obj);\r
52505                     }\r
52506                     else {\r
52507                         setTimeout(arguments.callee, 10);\r
52508                     }\r
52509                 })();\r
52510             }\r
52511             createSWF(att, par, replaceElemIdStr);\r
52512         }\r
52513     }\r
52514     \r
52515     /* Functions to abstract and display alternative content\r
52516     */\r
52517     function displayAltContent(obj) {\r
52518         if (ua.ie && ua.win && obj.readyState != 4) {\r
52519             // IE only: when a SWF is loading (AND: not available in cache) wait for the readyState of the object element to become 4 before removing it,\r
52520             // because you cannot properly cancel a loading SWF file without breaking browser load references, also obj.onreadystatechange doesn't work\r
52521             var el = createElement("div");\r
52522             obj.parentNode.insertBefore(el, obj); // insert placeholder div that will be replaced by the alternative content\r
52523             el.parentNode.replaceChild(abstractAltContent(obj), el);\r
52524             obj.style.display = "none";\r
52525             (function(){\r
52526                 if (obj.readyState == 4) {\r
52527                     obj.parentNode.removeChild(obj);\r
52528                 }\r
52529                 else {\r
52530                     setTimeout(arguments.callee, 10);\r
52531                 }\r
52532             })();\r
52533         }\r
52534         else {\r
52535             obj.parentNode.replaceChild(abstractAltContent(obj), obj);\r
52536         }\r
52537     } \r
52538 \r
52539     function abstractAltContent(obj) {\r
52540         var ac = createElement("div");\r
52541         if (ua.win && ua.ie) {\r
52542             ac.innerHTML = obj.innerHTML;\r
52543         }\r
52544         else {\r
52545             var nestedObj = obj.getElementsByTagName(OBJECT)[0];\r
52546             if (nestedObj) {\r
52547                 var c = nestedObj.childNodes;\r
52548                 if (c) {\r
52549                     var cl = c.length;\r
52550                     for (var i = 0; i < cl; i++) {\r
52551                         if (!(c[i].nodeType == 1 && c[i].nodeName == "PARAM") && !(c[i].nodeType == 8)) {\r
52552                             ac.appendChild(c[i].cloneNode(true));\r
52553                         }\r
52554                     }\r
52555                 }\r
52556             }\r
52557         }\r
52558         return ac;\r
52559     }\r
52560     \r
52561     /* Cross-browser dynamic SWF creation\r
52562     */\r
52563     function createSWF(attObj, parObj, id) {\r
52564         var r, el = getElementById(id);\r
52565         if (ua.wk && ua.wk < 312) { return r; }\r
52566         if (el) {\r
52567             if (typeof attObj.id == UNDEF) { // if no 'id' is defined for the object element, it will inherit the 'id' from the alternative content\r
52568                 attObj.id = id;\r
52569             }\r
52570             if (ua.ie && ua.win) { // Internet Explorer + the HTML object element + W3C DOM methods do not combine: fall back to outerHTML\r
52571                 var att = "";\r
52572                 for (var i in attObj) {\r
52573                     if (attObj[i] != Object.prototype[i]) { // filter out prototype additions from other potential libraries\r
52574                         if (i.toLowerCase() == "data") {\r
52575                             parObj.movie = attObj[i];\r
52576                         }\r
52577                         else if (i.toLowerCase() == "styleclass") { // 'class' is an ECMA4 reserved keyword\r
52578                             att += ' class="' + attObj[i] + '"';\r
52579                         }\r
52580                         else if (i.toLowerCase() != "classid") {\r
52581                             att += ' ' + i + '="' + attObj[i] + '"';\r
52582                         }\r
52583                     }\r
52584                 }\r
52585                 var par = "";\r
52586                 for (var j in parObj) {\r
52587                     if (parObj[j] != Object.prototype[j]) { // filter out prototype additions from other potential libraries\r
52588                         par += '<param name="' + j + '" value="' + parObj[j] + '" />';\r
52589                     }\r
52590                 }\r
52591                 el.outerHTML = '<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"' + att + '>' + par + '</object>';\r
52592                 objIdArr[objIdArr.length] = attObj.id; // stored to fix object 'leaks' on unload (dynamic publishing only)\r
52593                 r = getElementById(attObj.id);  \r
52594             }\r
52595             else { // well-behaving browsers\r
52596                 var o = createElement(OBJECT);\r
52597                 o.setAttribute("type", FLASH_MIME_TYPE);\r
52598                 for (var m in attObj) {\r
52599                     if (attObj[m] != Object.prototype[m]) { // filter out prototype additions from other potential libraries\r
52600                         if (m.toLowerCase() == "styleclass") { // 'class' is an ECMA4 reserved keyword\r
52601                             o.setAttribute("class", attObj[m]);\r
52602                         }\r
52603                         else if (m.toLowerCase() != "classid") { // filter out IE specific attribute\r
52604                             o.setAttribute(m, attObj[m]);\r
52605                         }\r
52606                     }\r
52607                 }\r
52608                 for (var n in parObj) {\r
52609                     if (parObj[n] != Object.prototype[n] && n.toLowerCase() != "movie") { // filter out prototype additions from other potential libraries and IE specific param element\r
52610                         createObjParam(o, n, parObj[n]);\r
52611                     }\r
52612                 }\r
52613                 el.parentNode.replaceChild(o, el);\r
52614                 r = o;\r
52615             }\r
52616         }\r
52617         return r;\r
52618     }\r
52619     \r
52620     function createObjParam(el, pName, pValue) {\r
52621         var p = createElement("param");\r
52622         p.setAttribute("name", pName);  \r
52623         p.setAttribute("value", pValue);\r
52624         el.appendChild(p);\r
52625     }\r
52626     \r
52627     /* Cross-browser SWF removal\r
52628         - Especially needed to safely and completely remove a SWF in Internet Explorer\r
52629     */\r
52630     function removeSWF(id) {\r
52631         var obj = getElementById(id);\r
52632         if (obj && obj.nodeName == "OBJECT") {\r
52633             if (ua.ie && ua.win) {\r
52634                 obj.style.display = "none";\r
52635                 (function(){\r
52636                     if (obj.readyState == 4) {\r
52637                         removeObjectInIE(id);\r
52638                     }\r
52639                     else {\r
52640                         setTimeout(arguments.callee, 10);\r
52641                     }\r
52642                 })();\r
52643             }\r
52644             else {\r
52645                 obj.parentNode.removeChild(obj);\r
52646             }\r
52647         }\r
52648     }\r
52649     \r
52650     function removeObjectInIE(id) {\r
52651         var obj = getElementById(id);\r
52652         if (obj) {\r
52653             for (var i in obj) {\r
52654                 if (typeof obj[i] == "function") {\r
52655                     obj[i] = null;\r
52656                 }\r
52657             }\r
52658             obj.parentNode.removeChild(obj);\r
52659         }\r
52660     }\r
52661     \r
52662     /* Functions to optimize JavaScript compression\r
52663     */\r
52664     function getElementById(id) {\r
52665         var el = null;\r
52666         try {\r
52667             el = doc.getElementById(id);\r
52668         }\r
52669         catch (e) {}\r
52670         return el;\r
52671     }\r
52672     \r
52673     function createElement(el) {\r
52674         return doc.createElement(el);\r
52675     }\r
52676     \r
52677     /* Updated attachEvent function for Internet Explorer\r
52678         - Stores attachEvent information in an Array, so on unload the detachEvent functions can be called to avoid memory leaks\r
52679     */  \r
52680     function addListener(target, eventType, fn) {\r
52681         target.attachEvent(eventType, fn);\r
52682         listenersArr[listenersArr.length] = [target, eventType, fn];\r
52683     }\r
52684     \r
52685     /* Flash Player and SWF content version matching\r
52686     */\r
52687     function hasPlayerVersion(rv) {\r
52688         var pv = ua.pv, v = rv.split(".");\r
52689         v[0] = parseInt(v[0], 10);\r
52690         v[1] = parseInt(v[1], 10) || 0; // supports short notation, e.g. "9" instead of "9.0.0"\r
52691         v[2] = parseInt(v[2], 10) || 0;\r
52692         return (pv[0] > v[0] || (pv[0] == v[0] && pv[1] > v[1]) || (pv[0] == v[0] && pv[1] == v[1] && pv[2] >= v[2])) ? true : false;\r
52693     }\r
52694     \r
52695     /* Cross-browser dynamic CSS creation\r
52696         - Based on Bobby van der Sluis' solution: http://www.bobbyvandersluis.com/articles/dynamicCSS.php\r
52697     */  \r
52698     function createCSS(sel, decl, media, newStyle) {\r
52699         if (ua.ie && ua.mac) { return; }\r
52700         var h = doc.getElementsByTagName("head")[0];\r
52701         if (!h) { return; } // to also support badly authored HTML pages that lack a head element\r
52702         var m = (media && typeof media == "string") ? media : "screen";\r
52703         if (newStyle) {\r
52704             dynamicStylesheet = null;\r
52705             dynamicStylesheetMedia = null;\r
52706         }\r
52707         if (!dynamicStylesheet || dynamicStylesheetMedia != m) { \r
52708             // create dynamic stylesheet + get a global reference to it\r
52709             var s = createElement("style");\r
52710             s.setAttribute("type", "text/css");\r
52711             s.setAttribute("media", m);\r
52712             dynamicStylesheet = h.appendChild(s);\r
52713             if (ua.ie && ua.win && typeof doc.styleSheets != UNDEF && doc.styleSheets.length > 0) {\r
52714                 dynamicStylesheet = doc.styleSheets[doc.styleSheets.length - 1];\r
52715             }\r
52716             dynamicStylesheetMedia = m;\r
52717         }\r
52718         // add style rule\r
52719         if (ua.ie && ua.win) {\r
52720             if (dynamicStylesheet && typeof dynamicStylesheet.addRule == OBJECT) {\r
52721                 dynamicStylesheet.addRule(sel, decl);\r
52722             }\r
52723         }\r
52724         else {\r
52725             if (dynamicStylesheet && typeof doc.createTextNode != UNDEF) {\r
52726                 dynamicStylesheet.appendChild(doc.createTextNode(sel + " {" + decl + "}"));\r
52727             }\r
52728         }\r
52729     }\r
52730     \r
52731     function setVisibility(id, isVisible) {\r
52732         if (!autoHideShow) { return; }\r
52733         var v = isVisible ? "visible" : "hidden";\r
52734         if (isDomLoaded && getElementById(id)) {\r
52735             getElementById(id).style.visibility = v;\r
52736         }\r
52737         else {\r
52738             createCSS("#" + id, "visibility:" + v);\r
52739         }\r
52740     }\r
52741 \r
52742     /* Filter to avoid XSS attacks\r
52743     */\r
52744     function urlEncodeIfNecessary(s) {\r
52745         var regex = /[\\\"<>\.;]/;\r
52746         var hasBadChars = regex.exec(s) != null;\r
52747         return hasBadChars && typeof encodeURIComponent != UNDEF ? encodeURIComponent(s) : s;\r
52748     }\r
52749     \r
52750     /* Release memory to avoid memory leaks caused by closures, fix hanging audio/video threads and force open sockets/NetConnections to disconnect (Internet Explorer only)\r
52751     */\r
52752     var cleanup = function() {\r
52753         if (ua.ie && ua.win) {\r
52754             window.attachEvent("onunload", function() {\r
52755                 // remove listeners to avoid memory leaks\r
52756                 var ll = listenersArr.length;\r
52757                 for (var i = 0; i < ll; i++) {\r
52758                     listenersArr[i][0].detachEvent(listenersArr[i][1], listenersArr[i][2]);\r
52759                 }\r
52760                 // cleanup dynamically embedded objects to fix audio/video threads and force open sockets and NetConnections to disconnect\r
52761                 var il = objIdArr.length;\r
52762                 for (var j = 0; j < il; j++) {\r
52763                     removeSWF(objIdArr[j]);\r
52764                 }\r
52765                 // cleanup library's main closures to avoid memory leaks\r
52766                 for (var k in ua) {\r
52767                     ua[k] = null;\r
52768                 }\r
52769                 ua = null;\r
52770                 for (var l in swfobject) {\r
52771                     swfobject[l] = null;\r
52772                 }\r
52773                 swfobject = null;\r
52774             });\r
52775         }\r
52776     }();\r
52777     \r
52778     return {\r
52779         /* Public API\r
52780             - Reference: http://code.google.com/p/swfobject/wiki/documentation\r
52781         */ \r
52782         registerObject: function(objectIdStr, swfVersionStr, xiSwfUrlStr, callbackFn) {\r
52783             if (ua.w3 && objectIdStr && swfVersionStr) {\r
52784                 var regObj = {};\r
52785                 regObj.id = objectIdStr;\r
52786                 regObj.swfVersion = swfVersionStr;\r
52787                 regObj.expressInstall = xiSwfUrlStr;\r
52788                 regObj.callbackFn = callbackFn;\r
52789                 regObjArr[regObjArr.length] = regObj;\r
52790                 setVisibility(objectIdStr, false);\r
52791             }\r
52792             else if (callbackFn) {\r
52793                 callbackFn({success:false, id:objectIdStr});\r
52794             }\r
52795         },\r
52796         \r
52797         getObjectById: function(objectIdStr) {\r
52798             if (ua.w3) {\r
52799                 return getObjectById(objectIdStr);\r
52800             }\r
52801         },\r
52802         \r
52803         embedSWF: function(swfUrlStr, replaceElemIdStr, widthStr, heightStr, swfVersionStr, xiSwfUrlStr, flashvarsObj, parObj, attObj, callbackFn) {\r
52804             var callbackObj = {success:false, id:replaceElemIdStr};\r
52805             if (ua.w3 && !(ua.wk && ua.wk < 312) && swfUrlStr && replaceElemIdStr && widthStr && heightStr && swfVersionStr) {\r
52806                 setVisibility(replaceElemIdStr, false);\r
52807                 addDomLoadEvent(function() {\r
52808                     widthStr += ""; // auto-convert to string\r
52809                     heightStr += "";\r
52810                     var att = {};\r
52811                     if (attObj && typeof attObj === OBJECT) {\r
52812                         for (var i in attObj) { // copy object to avoid the use of references, because web authors often reuse attObj for multiple SWFs\r
52813                             att[i] = attObj[i];\r
52814                         }\r
52815                     }\r
52816                     att.data = swfUrlStr;\r
52817                     att.width = widthStr;\r
52818                     att.height = heightStr;\r
52819                     var par = {}; \r
52820                     if (parObj && typeof parObj === OBJECT) {\r
52821                         for (var j in parObj) { // copy object to avoid the use of references, because web authors often reuse parObj for multiple SWFs\r
52822                             par[j] = parObj[j];\r
52823                         }\r
52824                     }\r
52825                     if (flashvarsObj && typeof flashvarsObj === OBJECT) {\r
52826                         for (var k in flashvarsObj) { // copy object to avoid the use of references, because web authors often reuse flashvarsObj for multiple SWFs\r
52827                             if (typeof par.flashvars != UNDEF) {\r
52828                                 par.flashvars += "&" + k + "=" + flashvarsObj[k];\r
52829                             }\r
52830                             else {\r
52831                                 par.flashvars = k + "=" + flashvarsObj[k];\r
52832                             }\r
52833                         }\r
52834                     }\r
52835                     if (hasPlayerVersion(swfVersionStr)) { // create SWF\r
52836                         var obj = createSWF(att, par, replaceElemIdStr);\r
52837                         if (att.id == replaceElemIdStr) {\r
52838                             setVisibility(replaceElemIdStr, true);\r
52839                         }\r
52840                         callbackObj.success = true;\r
52841                         callbackObj.ref = obj;\r
52842                     }\r
52843                     else if (xiSwfUrlStr && canExpressInstall()) { // show Adobe Express Install\r
52844                         att.data = xiSwfUrlStr;\r
52845                         showExpressInstall(att, par, replaceElemIdStr, callbackFn);\r
52846                         return;\r
52847                     }\r
52848                     else { // show alternative content\r
52849                         setVisibility(replaceElemIdStr, true);\r
52850                     }\r
52851                     if (callbackFn) { callbackFn(callbackObj); }\r
52852                 });\r
52853             }\r
52854             else if (callbackFn) { callbackFn(callbackObj); }\r
52855         },\r
52856         \r
52857         switchOffAutoHideShow: function() {\r
52858             autoHideShow = false;\r
52859         },\r
52860         \r
52861         ua: ua,\r
52862         \r
52863         getFlashPlayerVersion: function() {\r
52864             return { major:ua.pv[0], minor:ua.pv[1], release:ua.pv[2] };\r
52865         },\r
52866         \r
52867         hasFlashPlayerVersion: hasPlayerVersion,\r
52868         \r
52869         createSWF: function(attObj, parObj, replaceElemIdStr) {\r
52870             if (ua.w3) {\r
52871                 return createSWF(attObj, parObj, replaceElemIdStr);\r
52872             }\r
52873             else {\r
52874                 return undefined;\r
52875             }\r
52876         },\r
52877         \r
52878         showExpressInstall: function(att, par, replaceElemIdStr, callbackFn) {\r
52879             if (ua.w3 && canExpressInstall()) {\r
52880                 showExpressInstall(att, par, replaceElemIdStr, callbackFn);\r
52881             }\r
52882         },\r
52883         \r
52884         removeSWF: function(objElemIdStr) {\r
52885             if (ua.w3) {\r
52886                 removeSWF(objElemIdStr);\r
52887             }\r
52888         },\r
52889         \r
52890         createCSS: function(selStr, declStr, mediaStr, newStyleBoolean) {\r
52891             if (ua.w3) {\r
52892                 createCSS(selStr, declStr, mediaStr, newStyleBoolean);\r
52893             }\r
52894         },\r
52895         \r
52896         addDomLoadEvent: addDomLoadEvent,\r
52897         \r
52898         addLoadEvent: addLoadEvent,\r
52899         \r
52900         getQueryParamValue: function(param) {\r
52901             var q = doc.location.search || doc.location.hash;\r
52902             if (q) {\r
52903                 if (/\?/.test(q)) { q = q.split("?")[1]; } // strip question mark\r
52904                 if (param == null) {\r
52905                     return urlEncodeIfNecessary(q);\r
52906                 }\r
52907                 var pairs = q.split("&");\r
52908                 for (var i = 0; i < pairs.length; i++) {\r
52909                     if (pairs[i].substring(0, pairs[i].indexOf("=")) == param) {\r
52910                         return urlEncodeIfNecessary(pairs[i].substring((pairs[i].indexOf("=") + 1)));\r
52911                     }\r
52912                 }\r
52913             }\r
52914             return "";\r
52915         },\r
52916         \r
52917         // For internal usage only\r
52918         expressInstallCallback: function() {\r
52919             if (isExpressInstallActive) {\r
52920                 var obj = getElementById(EXPRESS_INSTALL_ID);\r
52921                 if (obj && storedAltContent) {\r
52922                     obj.parentNode.replaceChild(storedAltContent, obj);\r
52923                     if (storedAltContentId) {\r
52924                         setVisibility(storedAltContentId, true);\r
52925                         if (ua.ie && ua.win) { storedAltContent.style.display = "block"; }\r
52926                     }\r
52927                     if (storedCallbackFn) { storedCallbackFn(storedCallbackObj); }\r
52928                 }\r
52929                 isExpressInstallActive = false;\r
52930             } \r
52931         }\r
52932     };\r
52933 }();\r
52934 /**
52935  * @class Ext.FlashComponent
52936  * @extends Ext.BoxComponent
52937  * @constructor
52938  * @xtype flash
52939  */
52940 Ext.FlashComponent = Ext.extend(Ext.BoxComponent, {
52941     /**
52942      * @cfg {String} flashVersion
52943      * Indicates the version the flash content was published for. Defaults to <tt>'9.0.45'</tt>.
52944      */
52945     flashVersion : '9.0.115',
52946
52947     /**
52948      * @cfg {String} backgroundColor
52949      * The background color of the chart. Defaults to <tt>'#ffffff'</tt>.
52950      */
52951     backgroundColor: '#ffffff',
52952
52953     /**
52954      * @cfg {String} wmode
52955      * The wmode of the flash object. This can be used to control layering. Defaults to <tt>'opaque'</tt>.
52956      */
52957     wmode: 'opaque',
52958     
52959     /**
52960      * @cfg {Object} flashVars
52961      * A set of key value pairs to be passed to the flash object as flash variables. Defaults to <tt>undefined</tt>.
52962      */
52963     flashVars: undefined,
52964     
52965     /**
52966      * @cfg {Object} flashParams
52967      * A set of key value pairs to be passed to the flash object as parameters. Possible parameters can be found here:
52968      * http://kb2.adobe.com/cps/127/tn_12701.html Defaults to <tt>undefined</tt>.
52969      */
52970     flashParams: undefined,
52971
52972     /**
52973      * @cfg {String} url
52974      * The URL of the chart to include. Defaults to <tt>undefined</tt>.
52975      */
52976     url: undefined,
52977     swfId : undefined,
52978     swfWidth: '100%',
52979     swfHeight: '100%',
52980
52981     /**
52982      * @cfg {Boolean} expressInstall
52983      * True to prompt the user to install flash if not installed. Note that this uses
52984      * Ext.FlashComponent.EXPRESS_INSTALL_URL, which should be set to the local resource. Defaults to <tt>false</tt>.
52985      */
52986     expressInstall: false,
52987
52988     initComponent : function(){
52989         Ext.FlashComponent.superclass.initComponent.call(this);
52990
52991         this.addEvents(
52992             /**
52993              * @event initialize
52994              * 
52995              * @param {Chart} this
52996              */
52997             'initialize'
52998         );
52999     },
53000
53001     onRender : function(){
53002         Ext.FlashComponent.superclass.onRender.apply(this, arguments);
53003
53004         var params = Ext.apply({
53005             allowScriptAccess: 'always',
53006             bgcolor: this.backgroundColor,
53007             wmode: this.wmode
53008         }, this.flashParams), vars = Ext.apply({
53009             allowedDomain: document.location.hostname,
53010             elementID: this.getId(),
53011             eventHandler: 'Ext.FlashEventProxy.onEvent'
53012         }, this.flashVars);
53013
53014         new swfobject.embedSWF(this.url, this.id, this.swfWidth, this.swfHeight, this.flashVersion,
53015             this.expressInstall ? Ext.FlashComponent.EXPRESS_INSTALL_URL : undefined, vars, params);
53016
53017         this.swf = Ext.getDom(this.id);
53018         this.el = Ext.get(this.swf);
53019     },
53020
53021     getSwfId : function(){
53022         return this.swfId || (this.swfId = "extswf" + (++Ext.Component.AUTO_ID));
53023     },
53024
53025     getId : function(){
53026         return this.id || (this.id = "extflashcmp" + (++Ext.Component.AUTO_ID));
53027     },
53028
53029     onFlashEvent : function(e){
53030         switch(e.type){
53031             case "swfReady":
53032                 this.initSwf();
53033                 return;
53034             case "log":
53035                 return;
53036         }
53037         e.component = this;
53038         this.fireEvent(e.type.toLowerCase().replace(/event$/, ''), e);
53039     },
53040
53041     initSwf : function(){
53042         this.onSwfReady(!!this.isInitialized);
53043         this.isInitialized = true;
53044         this.fireEvent('initialize', this);
53045     },
53046
53047     beforeDestroy: function(){
53048         if(this.rendered){
53049             swfobject.removeSWF(this.swf.id);
53050         }
53051         Ext.FlashComponent.superclass.beforeDestroy.call(this);
53052     },
53053
53054     onSwfReady : Ext.emptyFn
53055 });
53056
53057 /**
53058  * Sets the url for installing flash if it doesn't exist. This should be set to a local resource.
53059  * @static
53060  * @type String
53061  */
53062 Ext.FlashComponent.EXPRESS_INSTALL_URL = 'http:/' + '/swfobject.googlecode.com/svn/trunk/swfobject/expressInstall.swf';
53063
53064 Ext.reg('flash', Ext.FlashComponent);/**\r
53065  * @class Ext.FlashProxy\r
53066  * @singleton\r
53067  */\r
53068 Ext.FlashEventProxy = {\r
53069     onEvent : function(id, e){\r
53070         var fp = Ext.getCmp(id);\r
53071         if(fp){\r
53072             fp.onFlashEvent(e);\r
53073         }else{\r
53074             arguments.callee.defer(10, this, [id, e]);\r
53075         }\r
53076     }\r
53077 }/**\r
53078  * @class Ext.chart.Chart\r
53079  * @extends Ext.FlashComponent\r
53080  * The Ext.chart package provides the capability to visualize data with flash based charting.\r
53081  * Each chart binds directly to an Ext.data.Store enabling automatic updates of the chart.\r
53082  * To change the look and feel of a chart, see the {@link #chartStyle} and {@link #extraStyle} config options.\r
53083  * @constructor\r
53084  * @xtype chart\r
53085  */\r
53086  \r
53087  Ext.chart.Chart = Ext.extend(Ext.FlashComponent, {\r
53088     refreshBuffer: 100,\r
53089     \r
53090     /**\r
53091      * @cfg {String} backgroundColor\r
53092      * @hide\r
53093      */\r
53094 \r
53095     /**\r
53096      * @cfg {Object} chartStyle\r
53097      * Sets styles for this chart. This contains default styling, so modifying this property will <b>override</b>\r
53098      * the built in styles of the chart. Use {@link #extraStyle} to add customizations to the default styling. \r
53099      */\r
53100     chartStyle: {\r
53101         padding: 10,\r
53102         animationEnabled: true,\r
53103         font: {\r
53104             name: 'Tahoma',\r
53105             color: 0x444444,\r
53106             size: 11\r
53107         },\r
53108         dataTip: {\r
53109             padding: 5,\r
53110             border: {\r
53111                 color: 0x99bbe8,\r
53112                 size:1\r
53113             },\r
53114             background: {\r
53115                 color: 0xDAE7F6,\r
53116                 alpha: .9\r
53117             },\r
53118             font: {\r
53119                 name: 'Tahoma',\r
53120                 color: 0x15428B,\r
53121                 size: 10,\r
53122                 bold: true\r
53123             }\r
53124         }\r
53125     },\r
53126     \r
53127     /**\r
53128      * @cfg {String} url\r
53129      * The url to load the chart from. This defaults to Ext.chart.Chart.CHART_URL, which should\r
53130      * be modified to point to the local charts resource.\r
53131      */\r
53132     \r
53133     /**\r
53134      * @cfg {Object} extraStyle\r
53135      * Contains extra styles that will be added or overwritten to the default chartStyle. Defaults to <tt>null</tt>.\r
53136      * For a detailed list of the options available, visit the YUI Charts site \r
53137      * at <a href="http://developer.yahoo.com/yui/charts/#basicstyles">http://developer.yahoo.com/yui/charts/#basicstyles</a><br/>\r
53138      * Some of the options availabe:<br />\r
53139      * <ul style="padding:5px;padding-left:16px;list-style-type:inherit;">\r
53140      * <li><b>padding</b> - The space around the edge of the chart's contents. Padding does not increase the size of the chart.</li>\r
53141      * <li><b>animationEnabled</b> - A Boolean value that specifies whether marker animations are enabled or not. Enabled by default.</li>\r
53142      * <li><b>font</b> - An Object defining the font style to be used in the chart. Defaults to <tt>{ name: 'Tahoma', color: 0x444444, size: 11 }</tt><br/>\r
53143      *  <ul style="padding:5px;padding-left:26px;list-style-type:circle;">\r
53144      *      <li><b>name</b> - font name</li>\r
53145      *      <li><b>color</b> - font color (hex code, ie: "#ff0000", "ff0000" or 0xff0000)</li>\r
53146      *      <li><b>size</b> - font size in points (numeric portion only, ie: 11)</li>\r
53147      *      <li><b>bold</b> - boolean</li>\r
53148      *      <li><b>italic</b> - boolean</li>\r
53149      *      <li><b>underline</b> - boolean</li>\r
53150      *  </ul>\r
53151      * </li>\r
53152      * <li><b>border</b> - An object defining the border style around the chart. The chart itself will decrease in dimensions to accomodate the border.<br/>\r
53153      *  <ul style="padding:5px;padding-left:26px;list-style-type:circle;">\r
53154      *      <li><b>color</b> - border color (hex code, ie: "#ff0000", "ff0000" or 0xff0000)</li>\r
53155      *      <li><b>size</b> - border size in pixels (numeric portion only, ie: 1)</li>\r
53156      *  </ul>\r
53157      * </li>\r
53158      * <li><b>background</b> - An object defining the background style of the chart.<br/>\r
53159      *  <ul style="padding:5px;padding-left:26px;list-style-type:circle;">\r
53160      *      <li><b>color</b> - border color (hex code, ie: "#ff0000", "ff0000" or 0xff0000)</li>\r
53161      *      <li><b>image</b> - an image URL. May be relative to the current document or absolute.</li>\r
53162      *  </ul>\r
53163      * </li>\r
53164      * <li><b>legend</b> - An object defining the legend style<br/>\r
53165      *  <ul style="padding:5px;padding-left:26px;list-style-type:circle;">\r
53166      *      <li><b>display</b> - location of the legend. Possible values are "none", "left", "right", "top", and "bottom".</li>\r
53167      *      <li><b>spacing</b> - an image URL. May be relative to the current document or absolute.</li>\r
53168      *      <li><b>padding, border, background, font</b> - same options as described above.</li>\r
53169      *  </ul></li>\r
53170      * <li><b>dataTip</b> - An object defining the style of the data tip (tooltip).<br/>\r
53171      *  <ul style="padding:5px;padding-left:26px;list-style-type:circle;">\r
53172      *      <li><b>padding, border, background, font</b> - same options as described above.</li>\r
53173      *  </ul></li>\r
53174      * <li><b>xAxis and yAxis</b> - An object defining the style of the style of either axis.<br/>\r
53175      *  <ul style="padding:5px;padding-left:26px;list-style-type:circle;">\r
53176      *      <li><b>color</b> - same option as described above.</li>\r
53177      *      <li><b>size</b> - same option as described above.</li>\r
53178      *      <li><b>showLabels</b> - boolean</li>\r
53179      *      <li><b>labelRotation</b> - a value in degrees from -90 through 90. Default is zero.</li>\r
53180      *  </ul></li>\r
53181      * <li><b>majorGridLines and minorGridLines</b> - An object defining the style of the style of the grid lines.<br/>\r
53182      *  <ul style="padding:5px;padding-left:26px;list-style-type:circle;">\r
53183      *      <li><b>color, size</b> - same options as described above.</li>\r
53184      *  </ul></li></li>\r
53185      * <li><b>zeroGridLine</b> - An object defining the style of the style of the zero grid line.<br/>\r
53186      *  <ul style="padding:5px;padding-left:26px;list-style-type:circle;">\r
53187      *      <li><b>color, size</b> - same options as described above.</li>\r
53188      *  </ul></li></li>\r
53189      * <li><b>majorTicks and minorTicks</b> - An object defining the style of the style of ticks in the chart.<br/>\r
53190      *  <ul style="padding:5px;padding-left:26px;list-style-type:circle;">\r
53191      *      <li><b>color, size</b> - same options as described above.</li>\r
53192      *      <li><b>length</b> - the length of each tick in pixels extending from the axis.</li>\r
53193      *      <li><b>display</b> - how the ticks are drawn. Possible values are "none", "inside", "outside", and "cross".</li>\r
53194      *  </ul></li></li>\r
53195      * </ul>\r
53196      */\r
53197     extraStyle: null,\r
53198     \r
53199     /**\r
53200      * @cfg {Object} seriesStyles\r
53201      * Contains styles to apply to the series after a refresh. Defaults to <tt>null</tt>.\r
53202      */\r
53203     seriesStyles: null,\r
53204     \r
53205     /**\r
53206      * @cfg {Boolean} disableCaching\r
53207      * True to add a "cache buster" to the end of the chart url. Defaults to true for Opera and IE.\r
53208      */\r
53209     disableCaching: Ext.isIE || Ext.isOpera,\r
53210     disableCacheParam: '_dc',\r
53211 \r
53212     initComponent : function(){\r
53213         Ext.chart.Chart.superclass.initComponent.call(this);\r
53214         if(!this.url){\r
53215             this.url = Ext.chart.Chart.CHART_URL;\r
53216         }\r
53217         if(this.disableCaching){\r
53218             this.url = Ext.urlAppend(this.url, String.format('{0}={1}', this.disableCacheParam, new Date().getTime()));\r
53219         }\r
53220         this.addEvents(\r
53221             'itemmouseover',\r
53222             'itemmouseout',\r
53223             'itemclick',\r
53224             'itemdoubleclick',\r
53225             'itemdragstart',\r
53226             'itemdrag',\r
53227             'itemdragend',\r
53228             /**\r
53229              * @event beforerefresh\r
53230              * Fires before a refresh to the chart data is called.  If the beforerefresh handler returns\r
53231              * <tt>false</tt> the {@link #refresh} action will be cancelled.\r
53232              * @param {Chart} this\r
53233              */\r
53234             'beforerefresh',\r
53235             /**\r
53236              * @event refresh\r
53237              * Fires after the chart data has been refreshed.\r
53238              * @param {Chart} this\r
53239              */\r
53240             'refresh'\r
53241         );\r
53242         this.store = Ext.StoreMgr.lookup(this.store);\r
53243     },\r
53244 \r
53245     /**\r
53246      * Sets a single style value on the Chart instance.\r
53247      *\r
53248      * @param name {String} Name of the Chart style value to change.\r
53249      * @param value {Object} New value to pass to the Chart style.\r
53250      */\r
53251      setStyle: function(name, value){\r
53252          this.swf.setStyle(name, Ext.encode(value));\r
53253      },\r
53254 \r
53255     /**\r
53256      * Resets all styles on the Chart instance.\r
53257      *\r
53258      * @param styles {Object} Initializer for all Chart styles.\r
53259      */\r
53260     setStyles: function(styles){\r
53261         this.swf.setStyles(Ext.encode(styles));\r
53262     },\r
53263 \r
53264     /**\r
53265      * Sets the styles on all series in the Chart.\r
53266      *\r
53267      * @param styles {Array} Initializer for all Chart series styles.\r
53268      */\r
53269     setSeriesStyles: function(styles){\r
53270         this.seriesStyles = styles;\r
53271         var s = [];\r
53272         Ext.each(styles, function(style){\r
53273             s.push(Ext.encode(style));\r
53274         });\r
53275         this.swf.setSeriesStyles(s);\r
53276     },\r
53277 \r
53278     setCategoryNames : function(names){\r
53279         this.swf.setCategoryNames(names);\r
53280     },\r
53281 \r
53282     setTipRenderer : function(fn){\r
53283         var chart = this;\r
53284         this.tipFnName = this.createFnProxy(function(item, index, series){\r
53285             var record = chart.store.getAt(index);\r
53286             return fn(chart, record, index, series);\r
53287         }, this.tipFnName);\r
53288         this.swf.setDataTipFunction(this.tipFnName);\r
53289     },\r
53290 \r
53291     setSeries : function(series){\r
53292         this.series = series;\r
53293         this.refresh();\r
53294     },\r
53295 \r
53296     /**\r
53297      * Changes the data store bound to this chart and refreshes it.\r
53298      * @param {Store} store The store to bind to this chart\r
53299      */\r
53300     bindStore : function(store, initial){\r
53301         if(!initial && this.store){\r
53302             if(store !== this.store && this.store.autoDestroy){\r
53303                 this.store.destroy();\r
53304             }else{\r
53305                 this.store.un("datachanged", this.refresh, this);\r
53306                 this.store.un("add", this.delayRefresh, this);\r
53307                 this.store.un("remove", this.delayRefresh, this);\r
53308                 this.store.un("update", this.delayRefresh, this);\r
53309                 this.store.un("clear", this.refresh, this);\r
53310             }\r
53311         }\r
53312         if(store){\r
53313             store = Ext.StoreMgr.lookup(store);\r
53314             store.on({\r
53315                 scope: this,\r
53316                 datachanged: this.refresh,\r
53317                 add: this.delayRefresh,\r
53318                 remove: this.delayRefresh,\r
53319                 update: this.delayRefresh,\r
53320                 clear: this.refresh\r
53321             });\r
53322         }\r
53323         this.store = store;\r
53324         if(store && !initial){\r
53325             this.refresh();\r
53326         }\r
53327     },\r
53328 \r
53329     onSwfReady : function(isReset){\r
53330         Ext.chart.Chart.superclass.onSwfReady.call(this, isReset);\r
53331         this.swf.setType(this.type);\r
53332 \r
53333         if(this.chartStyle){\r
53334             this.setStyles(Ext.apply({}, this.extraStyle, this.chartStyle));\r
53335         }\r
53336 \r
53337         if(this.categoryNames){\r
53338             this.setCategoryNames(this.categoryNames);\r
53339         }\r
53340 \r
53341         if(this.tipRenderer){\r
53342             this.setTipRenderer(this.tipRenderer);\r
53343         }\r
53344         if(!isReset){\r
53345             this.bindStore(this.store, true);\r
53346         }\r
53347         this.refresh.defer(10, this);\r
53348     },\r
53349 \r
53350     delayRefresh : function(){\r
53351         if(!this.refreshTask){\r
53352             this.refreshTask = new Ext.util.DelayedTask(this.refresh, this);\r
53353         }\r
53354         this.refreshTask.delay(this.refreshBuffer);\r
53355     },\r
53356 \r
53357     refresh : function(){\r
53358         if(this.fireEvent('beforerefresh', this) !== false){\r
53359                 var styleChanged = false;\r
53360                 // convert the store data into something YUI charts can understand\r
53361                 var data = [], rs = this.store.data.items;\r
53362                 for(var j = 0, len = rs.length; j < len; j++){\r
53363                     data[j] = rs[j].data;\r
53364                 }\r
53365                 //make a copy of the series definitions so that we aren't\r
53366                 //editing them directly.\r
53367                 var dataProvider = [];\r
53368                 var seriesCount = 0;\r
53369                 var currentSeries = null;\r
53370                 var i = 0;\r
53371                 if(this.series){\r
53372                     seriesCount = this.series.length;\r
53373                     for(i = 0; i < seriesCount; i++){\r
53374                         currentSeries = this.series[i];\r
53375                         var clonedSeries = {};\r
53376                         for(var prop in currentSeries){\r
53377                             if(prop == "style" && currentSeries.style !== null){\r
53378                                 clonedSeries.style = Ext.encode(currentSeries.style);\r
53379                                 styleChanged = true;\r
53380                                 //we don't want to modify the styles again next time\r
53381                                 //so null out the style property.\r
53382                                 // this causes issues\r
53383                                 // currentSeries.style = null;\r
53384                             } else{\r
53385                                 clonedSeries[prop] = currentSeries[prop];\r
53386                             }\r
53387                         }\r
53388                         dataProvider.push(clonedSeries);\r
53389                     }\r
53390                 }\r
53391         \r
53392                 if(seriesCount > 0){\r
53393                     for(i = 0; i < seriesCount; i++){\r
53394                         currentSeries = dataProvider[i];\r
53395                         if(!currentSeries.type){\r
53396                             currentSeries.type = this.type;\r
53397                         }\r
53398                         currentSeries.dataProvider = data;\r
53399                     }\r
53400                 } else{\r
53401                     dataProvider.push({type: this.type, dataProvider: data});\r
53402                 }\r
53403                 this.swf.setDataProvider(dataProvider);\r
53404                 if(this.seriesStyles){\r
53405                     this.setSeriesStyles(this.seriesStyles);\r
53406                 }\r
53407             this.fireEvent('refresh', this);\r
53408         }\r
53409     },\r
53410 \r
53411     createFnProxy : function(fn, old){\r
53412         if(old){\r
53413             delete window[old];\r
53414         }\r
53415         var fnName = "extFnProxy" + (++Ext.chart.Chart.PROXY_FN_ID);\r
53416         window[fnName] = fn;\r
53417         return fnName;\r
53418     },\r
53419     \r
53420     onDestroy: function(){\r
53421         Ext.chart.Chart.superclass.onDestroy.call(this);\r
53422         this.bindStore(null);\r
53423         var tip = this.tipFnName;\r
53424         if(!Ext.isEmpty(tip)){\r
53425             delete window[tip];\r
53426         }\r
53427     }\r
53428 });\r
53429 Ext.reg('chart', Ext.chart.Chart);\r
53430 Ext.chart.Chart.PROXY_FN_ID = 0;\r
53431 \r
53432 /**\r
53433  * Sets the url to load the chart from. This should be set to a local resource.\r
53434  * @static\r
53435  * @type String\r
53436  */\r
53437 Ext.chart.Chart.CHART_URL = 'http:/' + '/yui.yahooapis.com/2.7.0/build/charts/assets/charts.swf';\r
53438 \r
53439 /**\r
53440  * @class Ext.chart.PieChart\r
53441  * @extends Ext.chart.Chart\r
53442  * @constructor\r
53443  * @xtype piechart\r
53444  */\r
53445 Ext.chart.PieChart = Ext.extend(Ext.chart.Chart, {\r
53446     type: 'pie',\r
53447 \r
53448     onSwfReady : function(isReset){\r
53449         Ext.chart.PieChart.superclass.onSwfReady.call(this, isReset);\r
53450 \r
53451         this.setDataField(this.dataField);\r
53452         this.setCategoryField(this.categoryField);\r
53453     },\r
53454 \r
53455     setDataField : function(field){\r
53456         this.dataField = field;\r
53457         this.swf.setDataField(field);\r
53458     },\r
53459 \r
53460     setCategoryField : function(field){\r
53461         this.categoryField = field;\r
53462         this.swf.setCategoryField(field);\r
53463     }\r
53464 });\r
53465 Ext.reg('piechart', Ext.chart.PieChart);\r
53466 \r
53467 /**\r
53468  * @class Ext.chart.CartesianChart\r
53469  * @extends Ext.chart.Chart\r
53470  * @constructor\r
53471  * @xtype cartesianchart\r
53472  */\r
53473 Ext.chart.CartesianChart = Ext.extend(Ext.chart.Chart, {\r
53474     onSwfReady : function(isReset){\r
53475         Ext.chart.CartesianChart.superclass.onSwfReady.call(this, isReset);\r
53476 \r
53477         if(this.xField){\r
53478             this.setXField(this.xField);\r
53479         }\r
53480         if(this.yField){\r
53481             this.setYField(this.yField);\r
53482         }\r
53483         if(this.xAxis){\r
53484             this.setXAxis(this.xAxis);\r
53485         }\r
53486         if(this.yAxis){\r
53487             this.setYAxis(this.yAxis);\r
53488         }\r
53489     },\r
53490 \r
53491     setXField : function(value){\r
53492         this.xField = value;\r
53493         this.swf.setHorizontalField(value);\r
53494     },\r
53495 \r
53496     setYField : function(value){\r
53497         this.yField = value;\r
53498         this.swf.setVerticalField(value);\r
53499     },\r
53500 \r
53501     setXAxis : function(value){\r
53502         this.xAxis = this.createAxis('xAxis', value);\r
53503         this.swf.setHorizontalAxis(this.xAxis);\r
53504     },\r
53505 \r
53506     setYAxis : function(value){\r
53507         this.yAxis = this.createAxis('yAxis', value);\r
53508         this.swf.setVerticalAxis(this.yAxis);\r
53509     },\r
53510 \r
53511     createAxis : function(axis, value){\r
53512         var o = Ext.apply({}, value), oldFn = null;\r
53513         if(this[axis]){\r
53514             oldFn = this[axis].labelFunction;\r
53515         }\r
53516         if(o.labelRenderer){\r
53517             var fn = o.labelRenderer;\r
53518             o.labelFunction = this.createFnProxy(function(v){\r
53519                 return fn(v);\r
53520             }, oldFn);\r
53521             delete o.labelRenderer;\r
53522         }\r
53523         return o;\r
53524     }\r
53525 });\r
53526 Ext.reg('cartesianchart', Ext.chart.CartesianChart);\r
53527 \r
53528 /**\r
53529  * @class Ext.chart.LineChart\r
53530  * @extends Ext.chart.CartesianChart\r
53531  * @constructor\r
53532  * @xtype linechart\r
53533  */\r
53534 Ext.chart.LineChart = Ext.extend(Ext.chart.CartesianChart, {\r
53535     type: 'line'\r
53536 });\r
53537 Ext.reg('linechart', Ext.chart.LineChart);\r
53538 \r
53539 /**\r
53540  * @class Ext.chart.ColumnChart\r
53541  * @extends Ext.chart.CartesianChart\r
53542  * @constructor\r
53543  * @xtype columnchart\r
53544  */\r
53545 Ext.chart.ColumnChart = Ext.extend(Ext.chart.CartesianChart, {\r
53546     type: 'column'\r
53547 });\r
53548 Ext.reg('columnchart', Ext.chart.ColumnChart);\r
53549 \r
53550 /**\r
53551  * @class Ext.chart.StackedColumnChart\r
53552  * @extends Ext.chart.CartesianChart\r
53553  * @constructor\r
53554  * @xtype stackedcolumnchart\r
53555  */\r
53556 Ext.chart.StackedColumnChart = Ext.extend(Ext.chart.CartesianChart, {\r
53557     type: 'stackcolumn'\r
53558 });\r
53559 Ext.reg('stackedcolumnchart', Ext.chart.StackedColumnChart);\r
53560 \r
53561 /**\r
53562  * @class Ext.chart.BarChart\r
53563  * @extends Ext.chart.CartesianChart\r
53564  * @constructor\r
53565  * @xtype barchart\r
53566  */\r
53567 Ext.chart.BarChart = Ext.extend(Ext.chart.CartesianChart, {\r
53568     type: 'bar'\r
53569 });\r
53570 Ext.reg('barchart', Ext.chart.BarChart);\r
53571 \r
53572 /**\r
53573  * @class Ext.chart.StackedBarChart\r
53574  * @extends Ext.chart.CartesianChart\r
53575  * @constructor\r
53576  * @xtype stackedbarchart\r
53577  */\r
53578 Ext.chart.StackedBarChart = Ext.extend(Ext.chart.CartesianChart, {\r
53579     type: 'stackbar'\r
53580 });\r
53581 Ext.reg('stackedbarchart', Ext.chart.StackedBarChart);\r
53582 \r
53583 \r
53584 \r
53585 /**\r
53586  * @class Ext.chart.Axis\r
53587  * Defines a CartesianChart's vertical or horizontal axis.\r
53588  * @constructor\r
53589  */\r
53590 Ext.chart.Axis = function(config){\r
53591     Ext.apply(this, config);\r
53592 };\r
53593 \r
53594 Ext.chart.Axis.prototype =\r
53595 {\r
53596     /**\r
53597      * The type of axis.\r
53598      *\r
53599      * @property type\r
53600      * @type String\r
53601      */\r
53602     type: null,\r
53603 \r
53604     /**\r
53605      * The direction in which the axis is drawn. May be "horizontal" or "vertical".\r
53606      *\r
53607      * @property orientation\r
53608      * @type String\r
53609      */\r
53610     orientation: "horizontal",\r
53611 \r
53612     /**\r
53613      * If true, the items on the axis will be drawn in opposite direction.\r
53614      *\r
53615      * @property reverse\r
53616      * @type Boolean\r
53617      */\r
53618     reverse: false,\r
53619 \r
53620     /**\r
53621      * A string reference to the globally-accessible function that may be called to\r
53622      * determine each of the label values for this axis.\r
53623      *\r
53624      * @property labelFunction\r
53625      * @type String\r
53626      */\r
53627     labelFunction: null,\r
53628 \r
53629     /**\r
53630      * If true, labels that overlap previously drawn labels on the axis will be hidden.\r
53631      *\r
53632      * @property hideOverlappingLabels\r
53633      * @type Boolean\r
53634      */\r
53635     hideOverlappingLabels: true\r
53636 };\r
53637 \r
53638 /**\r
53639  * @class Ext.chart.NumericAxis\r
53640  * @extends Ext.chart.Axis\r
53641  * A type of axis whose units are measured in numeric values.\r
53642  * @constructor\r
53643  */\r
53644 Ext.chart.NumericAxis = Ext.extend(Ext.chart.Axis, {\r
53645     type: "numeric",\r
53646 \r
53647     /**\r
53648      * The minimum value drawn by the axis. If not set explicitly, the axis minimum\r
53649      * will be calculated automatically.\r
53650      *\r
53651      * @property minimum\r
53652      * @type Number\r
53653      */\r
53654     minimum: NaN,\r
53655 \r
53656     /**\r
53657      * The maximum value drawn by the axis. If not set explicitly, the axis maximum\r
53658      * will be calculated automatically.\r
53659      *\r
53660      * @property maximum\r
53661      * @type Number\r
53662      */\r
53663     maximum: NaN,\r
53664 \r
53665     /**\r
53666      * The spacing between major intervals on this axis.\r
53667      *\r
53668      * @property majorUnit\r
53669      * @type Number\r
53670      */\r
53671     majorUnit: NaN,\r
53672 \r
53673     /**\r
53674      * The spacing between minor intervals on this axis.\r
53675      *\r
53676      * @property minorUnit\r
53677      * @type Number\r
53678      */\r
53679     minorUnit: NaN,\r
53680 \r
53681     /**\r
53682      * If true, the labels, ticks, gridlines, and other objects will snap to\r
53683      * the nearest major or minor unit. If false, their position will be based\r
53684      * on the minimum value.\r
53685      *\r
53686      * @property snapToUnits\r
53687      * @type Boolean\r
53688      */\r
53689     snapToUnits: true,\r
53690 \r
53691     /**\r
53692      * If true, and the bounds are calculated automatically, either the minimum or\r
53693      * maximum will be set to zero.\r
53694      *\r
53695      * @property alwaysShowZero\r
53696      * @type Boolean\r
53697      */\r
53698     alwaysShowZero: true,\r
53699 \r
53700     /**\r
53701      * The scaling algorithm to use on this axis. May be "linear" or "logarithmic".\r
53702      *\r
53703      * @property scale\r
53704      * @type String\r
53705      */\r
53706     scale: "linear"\r
53707 });\r
53708 \r
53709 /**\r
53710  * @class Ext.chart.TimeAxis\r
53711  * @extends Ext.chart.Axis\r
53712  * A type of axis whose units are measured in time-based values.\r
53713  * @constructor\r
53714  */\r
53715 Ext.chart.TimeAxis = Ext.extend(Ext.chart.Axis, {\r
53716     type: "time",\r
53717 \r
53718     /**\r
53719      * The minimum value drawn by the axis. If not set explicitly, the axis minimum\r
53720      * will be calculated automatically.\r
53721      *\r
53722      * @property minimum\r
53723      * @type Date\r
53724      */\r
53725     minimum: null,\r
53726 \r
53727     /**\r
53728      * The maximum value drawn by the axis. If not set explicitly, the axis maximum\r
53729      * will be calculated automatically.\r
53730      *\r
53731      * @property maximum\r
53732      * @type Number\r
53733      */\r
53734     maximum: null,\r
53735 \r
53736     /**\r
53737      * The spacing between major intervals on this axis.\r
53738      *\r
53739      * @property majorUnit\r
53740      * @type Number\r
53741      */\r
53742     majorUnit: NaN,\r
53743 \r
53744     /**\r
53745      * The time unit used by the majorUnit.\r
53746      *\r
53747      * @property majorTimeUnit\r
53748      * @type String\r
53749      */\r
53750     majorTimeUnit: null,\r
53751 \r
53752     /**\r
53753      * The spacing between minor intervals on this axis.\r
53754      *\r
53755      * @property majorUnit\r
53756      * @type Number\r
53757      */\r
53758     minorUnit: NaN,\r
53759 \r
53760     /**\r
53761      * The time unit used by the minorUnit.\r
53762      *\r
53763      * @property majorTimeUnit\r
53764      * @type String\r
53765      */\r
53766     minorTimeUnit: null,\r
53767 \r
53768     /**\r
53769      * If true, the labels, ticks, gridlines, and other objects will snap to\r
53770      * the nearest major or minor unit. If false, their position will be based\r
53771      * on the minimum value.\r
53772      *\r
53773      * @property snapToUnits\r
53774      * @type Boolean\r
53775      */\r
53776     snapToUnits: true\r
53777 });\r
53778 \r
53779 /**\r
53780  * @class Ext.chart.CategoryAxis\r
53781  * @extends Ext.chart.Axis\r
53782  * A type of axis that displays items in categories.\r
53783  * @constructor\r
53784  */\r
53785 Ext.chart.CategoryAxis = Ext.extend(Ext.chart.Axis, {\r
53786     type: "category",\r
53787 \r
53788     /**\r
53789      * A list of category names to display along this axis.\r
53790      *\r
53791      * @property categoryNames\r
53792      * @type Array\r
53793      */\r
53794     categoryNames: null\r
53795 });\r
53796 \r
53797 /**\r
53798  * @class Ext.chart.Series\r
53799  * Series class for the charts widget.\r
53800  * @constructor\r
53801  */\r
53802 Ext.chart.Series = function(config) { Ext.apply(this, config); };\r
53803 \r
53804 Ext.chart.Series.prototype =\r
53805 {\r
53806     /**\r
53807      * The type of series.\r
53808      *\r
53809      * @property type\r
53810      * @type String\r
53811      */\r
53812     type: null,\r
53813 \r
53814     /**\r
53815      * The human-readable name of the series.\r
53816      *\r
53817      * @property displayName\r
53818      * @type String\r
53819      */\r
53820     displayName: null\r
53821 };\r
53822 \r
53823 /**\r
53824  * @class Ext.chart.CartesianSeries\r
53825  * @extends Ext.chart.Series\r
53826  * CartesianSeries class for the charts widget.\r
53827  * @constructor\r
53828  */\r
53829 Ext.chart.CartesianSeries = Ext.extend(Ext.chart.Series, {\r
53830     /**\r
53831      * The field used to access the x-axis value from the items from the data source.\r
53832      *\r
53833      * @property xField\r
53834      * @type String\r
53835      */\r
53836     xField: null,\r
53837 \r
53838     /**\r
53839      * The field used to access the y-axis value from the items from the data source.\r
53840      *\r
53841      * @property yField\r
53842      * @type String\r
53843      */\r
53844     yField: null\r
53845 });\r
53846 \r
53847 /**\r
53848  * @class Ext.chart.ColumnSeries\r
53849  * @extends Ext.chart.CartesianSeries\r
53850  * ColumnSeries class for the charts widget.\r
53851  * @constructor\r
53852  */\r
53853 Ext.chart.ColumnSeries = Ext.extend(Ext.chart.CartesianSeries, {\r
53854     type: "column"\r
53855 });\r
53856 \r
53857 /**\r
53858  * @class Ext.chart.LineSeries\r
53859  * @extends Ext.chart.CartesianSeries\r
53860  * LineSeries class for the charts widget.\r
53861  * @constructor\r
53862  */\r
53863 Ext.chart.LineSeries = Ext.extend(Ext.chart.CartesianSeries, {\r
53864     type: "line"\r
53865 });\r
53866 \r
53867 /**\r
53868  * @class Ext.chart.BarSeries\r
53869  * @extends Ext.chart.CartesianSeries\r
53870  * BarSeries class for the charts widget.\r
53871  * @constructor\r
53872  */\r
53873 Ext.chart.BarSeries = Ext.extend(Ext.chart.CartesianSeries, {\r
53874     type: "bar"\r
53875 });\r
53876 \r
53877 \r
53878 /**\r
53879  * @class Ext.chart.PieSeries\r
53880  * @extends Ext.chart.Series\r
53881  * PieSeries class for the charts widget.\r
53882  * @constructor\r
53883  */\r
53884 Ext.chart.PieSeries = Ext.extend(Ext.chart.Series, {\r
53885     type: "pie",\r
53886     dataField: null,\r
53887     categoryField: null\r
53888 });/**
53889  * @class Ext.menu.Menu
53890  * @extends Ext.Container
53891  * <p>A menu object.  This is the container to which you may add menu items.  Menu can also serve as a base class
53892  * when you want a specialized menu based off of another component (like {@link Ext.menu.DateMenu} for example).</p>
53893  * <p>Menus may contain either {@link Ext.menu.Item menu items}, or general {@link Ext.Component Component}s.</p>
53894  * <p>To make a contained general {@link Ext.Component Component} line up with other {@link Ext.menu.Item menu items}
53895  * specify <tt>iconCls: 'no-icon'</tt>.  This reserves a space for an icon, and indents the Component in line
53896  * with the other menu items.  See {@link Ext.form.ComboBox}.{@link Ext.form.ComboBox#getListParent getListParent}
53897  * for an example.</p>
53898  * <p>By default, Menus are absolutely positioned, floating Components. By configuring a Menu with
53899  * <b><tt>{@link #floating}:false</tt></b>, a Menu may be used as child of a Container.</p>
53900  *
53901  * @xtype menu
53902  */
53903 Ext.menu.Menu = Ext.extend(Ext.Container, {
53904     /**
53905      * @cfg {Object} defaults
53906      * A config object that will be applied to all items added to this container either via the {@link #items}
53907      * config or via the {@link #add} method.  The defaults config can contain any number of
53908      * name/value property pairs to be added to each item, and should be valid for the types of items
53909      * being added to the menu.
53910      */
53911     /**
53912      * @cfg {Mixed} items
53913      * An array of items to be added to this menu. Menus may contain either {@link Ext.menu.Item menu items},
53914      * or general {@link Ext.Component Component}s.
53915      */
53916     /**
53917      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
53918      */
53919     minWidth : 120,
53920     /**
53921      * @cfg {Boolean/String} shadow True or 'sides' for the default effect, 'frame' for 4-way shadow, and 'drop'
53922      * for bottom-right shadow (defaults to 'sides')
53923      */
53924     shadow : 'sides',
53925     /**
53926      * @cfg {String} subMenuAlign The {@link Ext.Element#alignTo} anchor position value to use for submenus of
53927      * this menu (defaults to 'tl-tr?')
53928      */
53929     subMenuAlign : 'tl-tr?',
53930     /**
53931      * @cfg {String} defaultAlign The default {@link Ext.Element#alignTo} anchor position value for this menu
53932      * relative to its element of origin (defaults to 'tl-bl?')
53933      */
53934     defaultAlign : 'tl-bl?',
53935     /**
53936      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
53937      */
53938     allowOtherMenus : false,
53939     /**
53940      * @cfg {Boolean} ignoreParentClicks True to ignore clicks on any item in this menu that is a parent item (displays
53941      * a submenu) so that the submenu is not dismissed when clicking the parent item (defaults to false).
53942      */
53943     ignoreParentClicks : false,
53944     /**
53945      * @cfg {Boolean} enableScrolling True to allow the menu container to have scroller controls if the menu is too long (defaults to true).
53946      */
53947     enableScrolling : true,
53948     /**
53949      * @cfg {Number} maxHeight The maximum height of the menu. Only applies when enableScrolling is set to True (defaults to null).
53950      */
53951     maxHeight : null,
53952     /**
53953      * @cfg {Number} scrollIncrement The amount to scroll the menu. Only applies when enableScrolling is set to True (defaults to 24).
53954      */
53955     scrollIncrement : 24,
53956     /**
53957      * @cfg {Boolean} showSeparator True to show the icon separator. (defaults to true).
53958      */
53959     showSeparator : true,
53960     /**
53961      * @cfg {Array} defaultOffsets An array specifying the [x, y] offset in pixels by which to
53962      * change the default Menu popup position after aligning according to the {@link #defaultAlign}
53963      * configuration. Defaults to <tt>[0, 0]</tt>.
53964      */
53965     defaultOffsets : [0, 0],
53966
53967     /**
53968      * @cfg {Boolean} plain
53969      * True to remove the incised line down the left side of the menu. Defaults to <tt>false</tt>.
53970      */
53971     plain : false,
53972
53973     /**
53974      * @cfg {Boolean} floating
53975      * <p>By default, a Menu configured as <b><code>floating:true</code></b>
53976      * will be rendered as an {@link Ext.Layer} (an absolutely positioned,
53977      * floating Component with zindex=15000).
53978      * If configured as <b><code>floating:false</code></b>, the Menu may be
53979      * used as child item of another Container instead of a free-floating
53980      * {@link Ext.Layer Layer}.
53981      */
53982     floating : true,
53983
53984
53985     /**
53986      * @cfg {Number} zIndex
53987      * zIndex to use when the menu is floating.
53988      */
53989     zIndex: 15000,
53990
53991     // private
53992     hidden : true,
53993
53994     /**
53995      * @cfg {String/Object} layout
53996      * This class assigns a default layout (<code>layout:'<b>menu</b>'</code>).
53997      * Developers <i>may</i> override this configuration option if another layout is required.
53998      * See {@link Ext.Container#layout} for additional information.
53999      */
54000     layout : 'menu',
54001     hideMode : 'offsets',    // Important for laying out Components
54002     scrollerHeight : 8,
54003     autoLayout : true,       // Provided for backwards compat
54004     defaultType : 'menuitem',
54005     bufferResize : false,
54006
54007     initComponent : function(){
54008         if(Ext.isArray(this.initialConfig)){
54009             Ext.apply(this, {items:this.initialConfig});
54010         }
54011         this.addEvents(
54012             /**
54013              * @event click
54014              * Fires when this menu is clicked (or when the enter key is pressed while it is active)
54015              * @param {Ext.menu.Menu} this
54016             * @param {Ext.menu.Item} menuItem The menu item that was clicked
54017              * @param {Ext.EventObject} e
54018              */
54019             'click',
54020             /**
54021              * @event mouseover
54022              * Fires when the mouse is hovering over this menu
54023              * @param {Ext.menu.Menu} this
54024              * @param {Ext.EventObject} e
54025              * @param {Ext.menu.Item} menuItem The menu item that was clicked
54026              */
54027             'mouseover',
54028             /**
54029              * @event mouseout
54030              * Fires when the mouse exits this menu
54031              * @param {Ext.menu.Menu} this
54032              * @param {Ext.EventObject} e
54033              * @param {Ext.menu.Item} menuItem The menu item that was clicked
54034              */
54035             'mouseout',
54036             /**
54037              * @event itemclick
54038              * Fires when a menu item contained in this menu is clicked
54039              * @param {Ext.menu.BaseItem} baseItem The BaseItem that was clicked
54040              * @param {Ext.EventObject} e
54041              */
54042             'itemclick'
54043         );
54044         Ext.menu.MenuMgr.register(this);
54045         if(this.floating){
54046             Ext.EventManager.onWindowResize(this.hide, this);
54047         }else{
54048             if(this.initialConfig.hidden !== false){
54049                 this.hidden = false;
54050             }
54051             this.internalDefaults = {hideOnClick: false};
54052         }
54053         Ext.menu.Menu.superclass.initComponent.call(this);
54054         if(this.autoLayout){
54055             var fn = this.doLayout.createDelegate(this, []);
54056             this.on({
54057                 add: fn,
54058                 remove: fn
54059             });
54060         }
54061     },
54062
54063     //private
54064     getLayoutTarget : function() {
54065         return this.ul;
54066     },
54067
54068     // private
54069     onRender : function(ct, position){
54070         if(!ct){
54071             ct = Ext.getBody();
54072         }
54073
54074         var dh = {
54075             id: this.getId(),
54076             cls: 'x-menu ' + ((this.floating) ? 'x-menu-floating x-layer ' : '') + (this.cls || '') + (this.plain ? ' x-menu-plain' : '') + (this.showSeparator ? '' : ' x-menu-nosep'),
54077             style: this.style,
54078             cn: [
54079                 {tag: 'a', cls: 'x-menu-focus', href: '#', onclick: 'return false;', tabIndex: '-1'},
54080                 {tag: 'ul', cls: 'x-menu-list'}
54081             ]
54082         };
54083         if(this.floating){
54084             this.el = new Ext.Layer({
54085                 shadow: this.shadow,
54086                 dh: dh,
54087                 constrain: false,
54088                 parentEl: ct,
54089                 zindex: this.zIndex
54090             });
54091         }else{
54092             this.el = ct.createChild(dh);
54093         }
54094         Ext.menu.Menu.superclass.onRender.call(this, ct, position);
54095
54096         if(!this.keyNav){
54097             this.keyNav = new Ext.menu.MenuNav(this);
54098         }
54099         // generic focus element
54100         this.focusEl = this.el.child('a.x-menu-focus');
54101         this.ul = this.el.child('ul.x-menu-list');
54102         this.mon(this.ul, {
54103             scope: this,
54104             click: this.onClick,
54105             mouseover: this.onMouseOver,
54106             mouseout: this.onMouseOut
54107         });
54108         if(this.enableScrolling){
54109             this.mon(this.el, {
54110                 scope: this,
54111                 delegate: '.x-menu-scroller',
54112                 click: this.onScroll,
54113                 mouseover: this.deactivateActive
54114             });
54115         }
54116     },
54117
54118     // private
54119     findTargetItem : function(e){
54120         var t = e.getTarget('.x-menu-list-item', this.ul, true);
54121         if(t && t.menuItemId){
54122             return this.items.get(t.menuItemId);
54123         }
54124     },
54125
54126     // private
54127     onClick : function(e){
54128         var t = this.findTargetItem(e);
54129         if(t){
54130             if(t.isFormField){
54131                 this.setActiveItem(t);
54132             }else if(t instanceof Ext.menu.BaseItem){
54133                 if(t.menu && this.ignoreParentClicks){
54134                     t.expandMenu();
54135                     e.preventDefault();
54136                 }else if(t.onClick){
54137                     t.onClick(e);
54138                     this.fireEvent('click', this, t, e);
54139                 }
54140             }
54141         }
54142     },
54143
54144     // private
54145     setActiveItem : function(item, autoExpand){
54146         if(item != this.activeItem){
54147             this.deactivateActive();
54148             if((this.activeItem = item).isFormField){
54149                 item.focus();
54150             }else{
54151                 item.activate(autoExpand);
54152             }
54153         }else if(autoExpand){
54154             item.expandMenu();
54155         }
54156     },
54157
54158     deactivateActive : function(){
54159         var a = this.activeItem;
54160         if(a){
54161             if(a.isFormField){
54162                 //Fields cannot deactivate, but Combos must collapse
54163                 if(a.collapse){
54164                     a.collapse();
54165                 }
54166             }else{
54167                 a.deactivate();
54168             }
54169             delete this.activeItem;
54170         }
54171     },
54172
54173     // private
54174     tryActivate : function(start, step){
54175         var items = this.items;
54176         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
54177             var item = items.get(i);
54178             if(!item.disabled && (item.canActivate || item.isFormField)){
54179                 this.setActiveItem(item, false);
54180                 return item;
54181             }
54182         }
54183         return false;
54184     },
54185
54186     // private
54187     onMouseOver : function(e){
54188         var t = this.findTargetItem(e);
54189         if(t){
54190             if(t.canActivate && !t.disabled){
54191                 this.setActiveItem(t, true);
54192             }
54193         }
54194         this.over = true;
54195         this.fireEvent('mouseover', this, e, t);
54196     },
54197
54198     // private
54199     onMouseOut : function(e){
54200         var t = this.findTargetItem(e);
54201         if(t){
54202             if(t == this.activeItem && t.shouldDeactivate && t.shouldDeactivate(e)){
54203                 this.activeItem.deactivate();
54204                 delete this.activeItem;
54205             }
54206         }
54207         this.over = false;
54208         this.fireEvent('mouseout', this, e, t);
54209     },
54210
54211     // private
54212     onScroll : function(e, t){
54213         if(e){
54214             e.stopEvent();
54215         }
54216         var ul = this.ul.dom, top = Ext.fly(t).is('.x-menu-scroller-top');
54217         ul.scrollTop += this.scrollIncrement * (top ? -1 : 1);
54218         if(top ? ul.scrollTop <= 0 : ul.scrollTop + this.activeMax >= ul.scrollHeight){
54219            this.onScrollerOut(null, t);
54220         }
54221     },
54222
54223     // private
54224     onScrollerIn : function(e, t){
54225         var ul = this.ul.dom, top = Ext.fly(t).is('.x-menu-scroller-top');
54226         if(top ? ul.scrollTop > 0 : ul.scrollTop + this.activeMax < ul.scrollHeight){
54227             Ext.fly(t).addClass(['x-menu-item-active', 'x-menu-scroller-active']);
54228         }
54229     },
54230
54231     // private
54232     onScrollerOut : function(e, t){
54233         Ext.fly(t).removeClass(['x-menu-item-active', 'x-menu-scroller-active']);
54234     },
54235
54236     /**
54237      * If <code>{@link #floating}=true</code>, shows this menu relative to
54238      * another element using {@link #showat}, otherwise uses {@link Ext.Component#show}.
54239      * @param {Mixed} element The element to align to
54240      * @param {String} position (optional) The {@link Ext.Element#alignTo} anchor position to use in aligning to
54241      * the element (defaults to this.defaultAlign)
54242      * @param {Ext.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
54243      */
54244     show : function(el, pos, parentMenu){
54245         if(this.floating){
54246             this.parentMenu = parentMenu;
54247             if(!this.el){
54248                 this.render();
54249                 this.doLayout(false, true);
54250             }
54251             this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign, this.defaultOffsets), parentMenu);
54252         }else{
54253             Ext.menu.Menu.superclass.show.call(this);
54254         }
54255     },
54256
54257     /**
54258      * Displays this menu at a specific xy position and fires the 'show' event if a
54259      * handler for the 'beforeshow' event does not return false cancelling the operation.
54260      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
54261      * @param {Ext.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
54262      */
54263     showAt : function(xy, parentMenu){
54264         if(this.fireEvent('beforeshow', this) !== false){
54265             this.parentMenu = parentMenu;
54266             if(!this.el){
54267                 this.render();
54268             }
54269             if(this.enableScrolling){
54270                 // set the position so we can figure out the constrain value.
54271                 this.el.setXY(xy);
54272                 //constrain the value, keep the y coordinate the same
54273                 xy[1] = this.constrainScroll(xy[1]);
54274                 xy = [this.el.adjustForConstraints(xy)[0], xy[1]];
54275             }else{
54276                 //constrain to the viewport.
54277                 xy = this.el.adjustForConstraints(xy);
54278             }
54279             this.el.setXY(xy);
54280             this.el.show();
54281             Ext.menu.Menu.superclass.onShow.call(this);
54282             if(Ext.isIE){
54283                 // internal event, used so we don't couple the layout to the menu
54284                 this.fireEvent('autosize', this);
54285                 if(!Ext.isIE8){
54286                     this.el.repaint();
54287                 }
54288             }
54289             this.hidden = false;
54290             this.focus();
54291             this.fireEvent('show', this);
54292         }
54293     },
54294
54295     constrainScroll : function(y){
54296         var max, full = this.ul.setHeight('auto').getHeight(),
54297             returnY = y, normalY, parentEl, scrollTop, viewHeight;
54298         if(this.floating){
54299             parentEl = Ext.fly(this.el.dom.parentNode);
54300             scrollTop = parentEl.getScroll().top;
54301             viewHeight = parentEl.getViewSize().height;
54302             //Normalize y by the scroll position for the parent element.  Need to move it into the coordinate space
54303             //of the view.
54304             normalY = y - scrollTop;
54305             max = this.maxHeight ? this.maxHeight : viewHeight - normalY;
54306             if(full > viewHeight) {
54307                 max = viewHeight;
54308                 //Set returnY equal to (0,0) in view space by reducing y by the value of normalY
54309                 returnY = y - normalY;
54310             } else if(max < full) {
54311                 returnY = y - (full - max);
54312                 max = full;
54313             }
54314         }else{
54315             max = this.getHeight();
54316         }
54317         if(full > max && max > 0){
54318             this.activeMax = max - this.scrollerHeight * 2 - this.el.getFrameWidth('tb') - Ext.num(this.el.shadowOffset, 0);
54319             this.ul.setHeight(this.activeMax);
54320             this.createScrollers();
54321             this.el.select('.x-menu-scroller').setDisplayed('');
54322         }else{
54323             this.ul.setHeight(full);
54324             this.el.select('.x-menu-scroller').setDisplayed('none');
54325         }
54326         this.ul.dom.scrollTop = 0;
54327         return returnY;
54328     },
54329
54330     createScrollers : function(){
54331         if(!this.scroller){
54332             this.scroller = {
54333                 pos: 0,
54334                 top: this.el.insertFirst({
54335                     tag: 'div',
54336                     cls: 'x-menu-scroller x-menu-scroller-top',
54337                     html: '&#160;'
54338                 }),
54339                 bottom: this.el.createChild({
54340                     tag: 'div',
54341                     cls: 'x-menu-scroller x-menu-scroller-bottom',
54342                     html: '&#160;'
54343                 })
54344             };
54345             this.scroller.top.hover(this.onScrollerIn, this.onScrollerOut, this);
54346             this.scroller.topRepeater = new Ext.util.ClickRepeater(this.scroller.top, {
54347                 listeners: {
54348                     click: this.onScroll.createDelegate(this, [null, this.scroller.top], false)
54349                 }
54350             });
54351             this.scroller.bottom.hover(this.onScrollerIn, this.onScrollerOut, this);
54352             this.scroller.bottomRepeater = new Ext.util.ClickRepeater(this.scroller.bottom, {
54353                 listeners: {
54354                     click: this.onScroll.createDelegate(this, [null, this.scroller.bottom], false)
54355                 }
54356             });
54357         }
54358     },
54359
54360     onLayout : function(){
54361         if(this.isVisible()){
54362             if(this.enableScrolling){
54363                 this.constrainScroll(this.el.getTop());
54364             }
54365             if(this.floating){
54366                 this.el.sync();
54367             }
54368         }
54369     },
54370
54371     focus : function(){
54372         if(!this.hidden){
54373             this.doFocus.defer(50, this);
54374         }
54375     },
54376
54377     doFocus : function(){
54378         if(!this.hidden){
54379             this.focusEl.focus();
54380         }
54381     },
54382
54383     /**
54384      * Hides this menu and optionally all parent menus
54385      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
54386      */
54387     hide : function(deep){
54388         if (!this.isDestroyed) {
54389             this.deepHide = deep;
54390             Ext.menu.Menu.superclass.hide.call(this);
54391             delete this.deepHide;
54392         }
54393     },
54394
54395     // private
54396     onHide : function(){
54397         Ext.menu.Menu.superclass.onHide.call(this);
54398         this.deactivateActive();
54399         if(this.el && this.floating){
54400             this.el.hide();
54401         }
54402         var pm = this.parentMenu;
54403         if(this.deepHide === true && pm){
54404             if(pm.floating){
54405                 pm.hide(true);
54406             }else{
54407                 pm.deactivateActive();
54408             }
54409         }
54410     },
54411
54412     // private
54413     lookupComponent : function(c){
54414          if(Ext.isString(c)){
54415             c = (c == 'separator' || c == '-') ? new Ext.menu.Separator() : new Ext.menu.TextItem(c);
54416              this.applyDefaults(c);
54417          }else{
54418             if(Ext.isObject(c)){
54419                 c = this.getMenuItem(c);
54420             }else if(c.tagName || c.el){ // element. Wrap it.
54421                 c = new Ext.BoxComponent({
54422                     el: c
54423                 });
54424             }
54425          }
54426          return c;
54427     },
54428
54429     applyDefaults : function(c){
54430         if(!Ext.isString(c)){
54431             c = Ext.menu.Menu.superclass.applyDefaults.call(this, c);
54432             var d = this.internalDefaults;
54433             if(d){
54434                 if(c.events){
54435                     Ext.applyIf(c.initialConfig, d);
54436                     Ext.apply(c, d);
54437                 }else{
54438                     Ext.applyIf(c, d);
54439                 }
54440             }
54441         }
54442         return c;
54443     },
54444
54445     // private
54446     getMenuItem : function(config){
54447        if(!config.isXType){
54448             if(!config.xtype && Ext.isBoolean(config.checked)){
54449                 return new Ext.menu.CheckItem(config)
54450             }
54451             return Ext.create(config, this.defaultType);
54452         }
54453         return config;
54454     },
54455
54456     /**
54457      * Adds a separator bar to the menu
54458      * @return {Ext.menu.Item} The menu item that was added
54459      */
54460     addSeparator : function(){
54461         return this.add(new Ext.menu.Separator());
54462     },
54463
54464     /**
54465      * Adds an {@link Ext.Element} object to the menu
54466      * @param {Mixed} el The element or DOM node to add, or its id
54467      * @return {Ext.menu.Item} The menu item that was added
54468      */
54469     addElement : function(el){
54470         return this.add(new Ext.menu.BaseItem({
54471             el: el
54472         }));
54473     },
54474
54475     /**
54476      * Adds an existing object based on {@link Ext.menu.BaseItem} to the menu
54477      * @param {Ext.menu.Item} item The menu item to add
54478      * @return {Ext.menu.Item} The menu item that was added
54479      */
54480     addItem : function(item){
54481         return this.add(item);
54482     },
54483
54484     /**
54485      * Creates a new {@link Ext.menu.Item} based an the supplied config object and adds it to the menu
54486      * @param {Object} config A MenuItem config object
54487      * @return {Ext.menu.Item} The menu item that was added
54488      */
54489     addMenuItem : function(config){
54490         return this.add(this.getMenuItem(config));
54491     },
54492
54493     /**
54494      * Creates a new {@link Ext.menu.TextItem} with the supplied text and adds it to the menu
54495      * @param {String} text The text to display in the menu item
54496      * @return {Ext.menu.Item} The menu item that was added
54497      */
54498     addText : function(text){
54499         return this.add(new Ext.menu.TextItem(text));
54500     },
54501
54502     //private
54503     onDestroy : function(){
54504         Ext.EventManager.removeResizeListener(this.hide, this);
54505         var pm = this.parentMenu;
54506         if(pm && pm.activeChild == this){
54507             delete pm.activeChild;
54508         }
54509         delete this.parentMenu;
54510         Ext.menu.Menu.superclass.onDestroy.call(this);
54511         Ext.menu.MenuMgr.unregister(this);
54512         if(this.keyNav) {
54513             this.keyNav.disable();
54514         }
54515         var s = this.scroller;
54516         if(s){
54517             Ext.destroy(s.topRepeater, s.bottomRepeater, s.top, s.bottom);
54518         }
54519         Ext.destroy(
54520             this.el,
54521             this.focusEl,
54522             this.ul
54523         );
54524     }
54525 });
54526
54527 Ext.reg('menu', Ext.menu.Menu);
54528
54529 // MenuNav is a private utility class used internally by the Menu
54530 Ext.menu.MenuNav = Ext.extend(Ext.KeyNav, function(){
54531     function up(e, m){
54532         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
54533             m.tryActivate(m.items.length-1, -1);
54534         }
54535     }
54536     function down(e, m){
54537         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
54538             m.tryActivate(0, 1);
54539         }
54540     }
54541     return {
54542         constructor : function(menu){
54543             Ext.menu.MenuNav.superclass.constructor.call(this, menu.el);
54544             this.scope = this.menu = menu;
54545         },
54546
54547         doRelay : function(e, h){
54548             var k = e.getKey();
54549 //          Keystrokes within a form Field (e.g.: down in a Combo) do not navigate. Allow only TAB
54550             if (this.menu.activeItem && this.menu.activeItem.isFormField && k != e.TAB) {
54551                 return false;
54552             }
54553             if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
54554                 this.menu.tryActivate(0, 1);
54555                 return false;
54556             }
54557             return h.call(this.scope || this, e, this.menu);
54558         },
54559
54560         tab: function(e, m) {
54561             e.stopEvent();
54562             if (e.shiftKey) {
54563                 up(e, m);
54564             } else {
54565                 down(e, m);
54566             }
54567         },
54568
54569         up : up,
54570
54571         down : down,
54572
54573         right : function(e, m){
54574             if(m.activeItem){
54575                 m.activeItem.expandMenu(true);
54576             }
54577         },
54578
54579         left : function(e, m){
54580             m.hide();
54581             if(m.parentMenu && m.parentMenu.activeItem){
54582                 m.parentMenu.activeItem.activate();
54583             }
54584         },
54585
54586         enter : function(e, m){
54587             if(m.activeItem){
54588                 e.stopPropagation();
54589                 m.activeItem.onClick(e);
54590                 m.fireEvent('click', this, m.activeItem);
54591                 return true;
54592             }
54593         }
54594     };
54595 }());
54596 /**
54597  * @class Ext.menu.MenuMgr
54598  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
54599  * @singleton
54600  */
54601 Ext.menu.MenuMgr = function(){
54602    var menus, active, groups = {}, attached = false, lastShow = new Date();
54603
54604    // private - called when first menu is created
54605    function init(){
54606        menus = {};
54607        active = new Ext.util.MixedCollection();
54608        Ext.getDoc().addKeyListener(27, function(){
54609            if(active.length > 0){
54610                hideAll();
54611            }
54612        });
54613    }
54614
54615    // private
54616    function hideAll(){
54617        if(active && active.length > 0){
54618            var c = active.clone();
54619            c.each(function(m){
54620                m.hide();
54621            });
54622            return true;
54623        }
54624        return false;
54625    }
54626
54627    // private
54628    function onHide(m){
54629        active.remove(m);
54630        if(active.length < 1){
54631            Ext.getDoc().un("mousedown", onMouseDown);
54632            attached = false;
54633        }
54634    }
54635
54636    // private
54637    function onShow(m){
54638        var last = active.last();
54639        lastShow = new Date();
54640        active.add(m);
54641        if(!attached){
54642            Ext.getDoc().on("mousedown", onMouseDown);
54643            attached = true;
54644        }
54645        if(m.parentMenu){
54646           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
54647           m.parentMenu.activeChild = m;
54648        }else if(last && !last.isDestroyed && last.isVisible()){
54649           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
54650        }
54651    }
54652
54653    // private
54654    function onBeforeHide(m){
54655        if(m.activeChild){
54656            m.activeChild.hide();
54657        }
54658        if(m.autoHideTimer){
54659            clearTimeout(m.autoHideTimer);
54660            delete m.autoHideTimer;
54661        }
54662    }
54663
54664    // private
54665    function onBeforeShow(m){
54666        var pm = m.parentMenu;
54667        if(!pm && !m.allowOtherMenus){
54668            hideAll();
54669        }else if(pm && pm.activeChild){
54670            pm.activeChild.hide();
54671        }
54672    }
54673
54674    // private
54675    function onMouseDown(e){
54676        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
54677            hideAll();
54678        }
54679    }
54680
54681    // private
54682    function onBeforeCheck(mi, state){
54683        if(state){
54684            var g = groups[mi.group];
54685            for(var i = 0, l = g.length; i < l; i++){
54686                if(g[i] != mi){
54687                    g[i].setChecked(false);
54688                }
54689            }
54690        }
54691    }
54692
54693    return {
54694
54695        /**
54696         * Hides all menus that are currently visible
54697         * @return {Boolean} success True if any active menus were hidden.
54698         */
54699        hideAll : function(){
54700             return hideAll();
54701        },
54702
54703        // private
54704        register : function(menu){
54705            if(!menus){
54706                init();
54707            }
54708            menus[menu.id] = menu;
54709            menu.on({
54710                beforehide: onBeforeHide,
54711                hide: onHide,
54712                beforeshow: onBeforeShow,
54713                show: onShow
54714            });
54715        },
54716
54717         /**
54718          * Returns a {@link Ext.menu.Menu} object
54719          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
54720          * be used to generate and return a new Menu instance.
54721          * @return {Ext.menu.Menu} The specified menu, or null if none are found
54722          */
54723        get : function(menu){
54724            if(typeof menu == "string"){ // menu id
54725                if(!menus){  // not initialized, no menus to return
54726                    return null;
54727                }
54728                return menus[menu];
54729            }else if(menu.events){  // menu instance
54730                return menu;
54731            }else if(typeof menu.length == 'number'){ // array of menu items?
54732                return new Ext.menu.Menu({items:menu});
54733            }else{ // otherwise, must be a config
54734                return Ext.create(menu, 'menu');
54735            }
54736        },
54737
54738        // private
54739        unregister : function(menu){
54740            delete menus[menu.id];
54741            menu.un("beforehide", onBeforeHide);
54742            menu.un("hide", onHide);
54743            menu.un("beforeshow", onBeforeShow);
54744            menu.un("show", onShow);
54745        },
54746
54747        // private
54748        registerCheckable : function(menuItem){
54749            var g = menuItem.group;
54750            if(g){
54751                if(!groups[g]){
54752                    groups[g] = [];
54753                }
54754                groups[g].push(menuItem);
54755                menuItem.on("beforecheckchange", onBeforeCheck);
54756            }
54757        },
54758
54759        // private
54760        unregisterCheckable : function(menuItem){
54761            var g = menuItem.group;
54762            if(g){
54763                groups[g].remove(menuItem);
54764                menuItem.un("beforecheckchange", onBeforeCheck);
54765            }
54766        },
54767
54768        getCheckedItem : function(groupId){
54769            var g = groups[groupId];
54770            if(g){
54771                for(var i = 0, l = g.length; i < l; i++){
54772                    if(g[i].checked){
54773                        return g[i];
54774                    }
54775                }
54776            }
54777            return null;
54778        },
54779
54780        setCheckedItem : function(groupId, itemId){
54781            var g = groups[groupId];
54782            if(g){
54783                for(var i = 0, l = g.length; i < l; i++){
54784                    if(g[i].id == itemId){
54785                        g[i].setChecked(true);
54786                    }
54787                }
54788            }
54789            return null;
54790        }
54791    };
54792 }();
54793 /**
54794  * @class Ext.menu.BaseItem
54795  * @extends Ext.Component
54796  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
54797  * management and base configuration options shared by all menu components.
54798  * @constructor
54799  * Creates a new BaseItem
54800  * @param {Object} config Configuration options
54801  * @xtype menubaseitem
54802  */
54803 Ext.menu.BaseItem = Ext.extend(Ext.Component, {
54804     /**
54805      * @property parentMenu
54806      * @type Ext.menu.Menu
54807      * The parent Menu of this Item.
54808      */
54809     /**
54810      * @cfg {Function} handler
54811      * A function that will handle the click event of this menu item (optional).
54812      * The handler is passed the following parameters:<div class="mdetail-params"><ul>
54813      * <li><code>b</code> : Item<div class="sub-desc">This menu Item.</div></li>
54814      * <li><code>e</code> : EventObject<div class="sub-desc">The click event.</div></li>
54815      * </ul></div>
54816      */
54817     /**
54818      * @cfg {Object} scope
54819      * The scope (<tt><b>this</b></tt> reference) in which the handler function will be called.
54820      */
54821     /**
54822      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
54823      */
54824     canActivate : false,
54825     /**
54826      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
54827      */
54828     activeClass : "x-menu-item-active",
54829     /**
54830      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
54831      */
54832     hideOnClick : true,
54833     /**
54834      * @cfg {Number} clickHideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
54835      */
54836     clickHideDelay : 1,
54837
54838     // private
54839     ctype : "Ext.menu.BaseItem",
54840
54841     // private
54842     actionMode : "container",
54843     
54844     initComponent : function(){
54845         Ext.menu.BaseItem.superclass.initComponent.call(this);
54846         this.addEvents(
54847                 /**
54848                  * @event click
54849                  * Fires when this item is clicked
54850                  * @param {Ext.menu.BaseItem} this
54851                  * @param {Ext.EventObject} e
54852                  */
54853                 'click',
54854                 /**
54855                  * @event activate
54856                  * Fires when this item is activated
54857                  * @param {Ext.menu.BaseItem} this
54858                  */
54859                 'activate',
54860                 /**
54861                  * @event deactivate
54862                  * Fires when this item is deactivated
54863                  * @param {Ext.menu.BaseItem} this
54864                  */
54865                 'deactivate'
54866             );
54867             if(this.handler){
54868                 this.on("click", this.handler, this.scope);
54869             }
54870     },
54871
54872     // private
54873     onRender : function(container, position){
54874         Ext.menu.BaseItem.superclass.onRender.apply(this, arguments);
54875         if(this.ownerCt && this.ownerCt instanceof Ext.menu.Menu){
54876             this.parentMenu = this.ownerCt;
54877         }else{
54878             this.container.addClass('x-menu-list-item');
54879             this.mon(this.el, {
54880                 scope: this,
54881                 click: this.onClick,
54882                 mouseenter: this.activate,
54883                 mouseleave: this.deactivate
54884             });
54885         }
54886     },
54887
54888     /**
54889      * Sets the function that will handle click events for this item (equivalent to passing in the {@link #handler}
54890      * config property).  If an existing handler is already registered, it will be unregistered for you.
54891      * @param {Function} handler The function that should be called on click
54892      * @param {Object} scope The scope (<code>this</code> reference) in which the handler function is executed. Defaults to this menu item.
54893      */
54894     setHandler : function(handler, scope){
54895         if(this.handler){
54896             this.un("click", this.handler, this.scope);
54897         }
54898         this.on("click", this.handler = handler, this.scope = scope);
54899     },
54900
54901     // private
54902     onClick : function(e){
54903         if(!this.disabled && this.fireEvent("click", this, e) !== false
54904                 && (this.parentMenu && this.parentMenu.fireEvent("itemclick", this, e) !== false)){
54905             this.handleClick(e);
54906         }else{
54907             e.stopEvent();
54908         }
54909     },
54910
54911     // private
54912     activate : function(){
54913         if(this.disabled){
54914             return false;
54915         }
54916         var li = this.container;
54917         li.addClass(this.activeClass);
54918         this.region = li.getRegion().adjust(2, 2, -2, -2);
54919         this.fireEvent("activate", this);
54920         return true;
54921     },
54922
54923     // private
54924     deactivate : function(){
54925         this.container.removeClass(this.activeClass);
54926         this.fireEvent("deactivate", this);
54927     },
54928
54929     // private
54930     shouldDeactivate : function(e){
54931         return !this.region || !this.region.contains(e.getPoint());
54932     },
54933
54934     // private
54935     handleClick : function(e){
54936         var pm = this.parentMenu;
54937         if(this.hideOnClick){
54938             if(pm.floating){
54939                 pm.hide.defer(this.clickHideDelay, pm, [true]);
54940             }else{
54941                 pm.deactivateActive();
54942             }
54943         }
54944     },
54945
54946     // private. Do nothing
54947     expandMenu : Ext.emptyFn,
54948
54949     // private. Do nothing
54950     hideMenu : Ext.emptyFn
54951 });
54952 Ext.reg('menubaseitem', Ext.menu.BaseItem);/**
54953  * @class Ext.menu.TextItem
54954  * @extends Ext.menu.BaseItem
54955  * Adds a static text string to a menu, usually used as either a heading or group separator.
54956  * @constructor
54957  * Creates a new TextItem
54958  * @param {Object/String} config If config is a string, it is used as the text to display, otherwise it
54959  * is applied as a config object (and should contain a <tt>text</tt> property).
54960  * @xtype menutextitem
54961  */
54962 Ext.menu.TextItem = Ext.extend(Ext.menu.BaseItem, {
54963     /**
54964      * @cfg {String} text The text to display for this item (defaults to '')
54965      */
54966     /**
54967      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
54968      */
54969     hideOnClick : false,
54970     /**
54971      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
54972      */
54973     itemCls : "x-menu-text",
54974     
54975     constructor : function(config){
54976         if(typeof config == 'string'){
54977             config = {text: config}
54978         }
54979         Ext.menu.TextItem.superclass.constructor.call(this, config);
54980     },
54981
54982     // private
54983     onRender : function(){
54984         var s = document.createElement("span");
54985         s.className = this.itemCls;
54986         s.innerHTML = this.text;
54987         this.el = s;
54988         Ext.menu.TextItem.superclass.onRender.apply(this, arguments);
54989     }
54990 });
54991 Ext.reg('menutextitem', Ext.menu.TextItem);/**
54992  * @class Ext.menu.Separator
54993  * @extends Ext.menu.BaseItem
54994  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
54995  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
54996  * @constructor
54997  * @param {Object} config Configuration options
54998  * @xtype menuseparator
54999  */
55000 Ext.menu.Separator = Ext.extend(Ext.menu.BaseItem, {
55001     /**
55002      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
55003      */
55004     itemCls : "x-menu-sep",
55005     /**
55006      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
55007      */
55008     hideOnClick : false,
55009     
55010     /** 
55011      * @cfg {String} activeClass
55012      * @hide 
55013      */
55014     activeClass: '',
55015
55016     // private
55017     onRender : function(li){
55018         var s = document.createElement("span");
55019         s.className = this.itemCls;
55020         s.innerHTML = "&#160;";
55021         this.el = s;
55022         li.addClass("x-menu-sep-li");
55023         Ext.menu.Separator.superclass.onRender.apply(this, arguments);
55024     }
55025 });
55026 Ext.reg('menuseparator', Ext.menu.Separator);/**\r
55027  * @class Ext.menu.Item\r
55028  * @extends Ext.menu.BaseItem\r
55029  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static\r
55030  * display items.  Item extends the base functionality of {@link Ext.menu.BaseItem} by adding menu-specific\r
55031  * activation and click handling.\r
55032  * @constructor\r
55033  * Creates a new Item\r
55034  * @param {Object} config Configuration options\r
55035  * @xtype menuitem\r
55036  */\r
55037 Ext.menu.Item = Ext.extend(Ext.menu.BaseItem, {\r
55038     /**\r
55039      * @property menu\r
55040      * @type Ext.menu.Menu\r
55041      * The submenu associated with this Item if one was configured.\r
55042      */\r
55043     /**\r
55044      * @cfg {Mixed} menu (optional) Either an instance of {@link Ext.menu.Menu} or the config object for an\r
55045      * {@link Ext.menu.Menu} which acts as the submenu when this item is activated.\r
55046      */\r
55047     /**\r
55048      * @cfg {String} icon The path to an icon to display in this item (defaults to Ext.BLANK_IMAGE_URL).  If\r
55049      * icon is specified {@link #iconCls} should not be.\r
55050      */\r
55051     /**\r
55052      * @cfg {String} iconCls A CSS class that specifies a background image that will be used as the icon for\r
55053      * this item (defaults to '').  If iconCls is specified {@link #icon} should not be.\r
55054      */\r
55055     /**\r
55056      * @cfg {String} text The text to display in this item (defaults to '').\r
55057      */\r
55058     /**\r
55059      * @cfg {String} href The href attribute to use for the underlying anchor link (defaults to '#').\r
55060      */\r
55061     /**\r
55062      * @cfg {String} hrefTarget The target attribute to use for the underlying anchor link (defaults to '').\r
55063      */\r
55064     /**\r
55065      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to 'x-menu-item')\r
55066      */\r
55067     itemCls : 'x-menu-item',\r
55068     /**\r
55069      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)\r
55070      */\r
55071     canActivate : true,\r
55072     /**\r
55073      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)\r
55074      */\r
55075     showDelay: 200,\r
55076     // doc'd in BaseItem\r
55077     hideDelay: 200,\r
55078 \r
55079     // private\r
55080     ctype: 'Ext.menu.Item',\r
55081 \r
55082     initComponent : function(){\r
55083         Ext.menu.Item.superclass.initComponent.call(this);\r
55084         if(this.menu){\r
55085             this.menu = Ext.menu.MenuMgr.get(this.menu);\r
55086             this.menu.ownerCt = this;\r
55087         }\r
55088     },\r
55089 \r
55090     // private\r
55091     onRender : function(container, position){\r
55092         if (!this.itemTpl) {\r
55093             this.itemTpl = Ext.menu.Item.prototype.itemTpl = new Ext.XTemplate(\r
55094                 '<a id="{id}" class="{cls}" hidefocus="true" unselectable="on" href="{href}"',\r
55095                     '<tpl if="hrefTarget">',\r
55096                         ' target="{hrefTarget}"',\r
55097                     '</tpl>',\r
55098                  '>',\r
55099                      '<img src="{icon}" class="x-menu-item-icon {iconCls}"/>',\r
55100                      '<span class="x-menu-item-text">{text}</span>',\r
55101                  '</a>'\r
55102              );\r
55103         }\r
55104         var a = this.getTemplateArgs();\r
55105         this.el = position ? this.itemTpl.insertBefore(position, a, true) : this.itemTpl.append(container, a, true);\r
55106         this.iconEl = this.el.child('img.x-menu-item-icon');\r
55107         this.textEl = this.el.child('.x-menu-item-text');\r
55108         if(!this.href) { // if no link defined, prevent the default anchor event\r
55109             this.mon(this.el, 'click', Ext.emptyFn, null, { preventDefault: true });\r
55110         }\r
55111         Ext.menu.Item.superclass.onRender.call(this, container, position);\r
55112     },\r
55113 \r
55114     getTemplateArgs: function() {\r
55115         return {\r
55116             id: this.id,\r
55117             cls: this.itemCls + (this.menu ?  ' x-menu-item-arrow' : '') + (this.cls ?  ' ' + this.cls : ''),\r
55118             href: this.href || '#',\r
55119             hrefTarget: this.hrefTarget,\r
55120             icon: this.icon || Ext.BLANK_IMAGE_URL,\r
55121             iconCls: this.iconCls || '',\r
55122             text: this.itemText||this.text||'&#160;'\r
55123         };\r
55124     },\r
55125 \r
55126     /**\r
55127      * Sets the text to display in this menu item\r
55128      * @param {String} text The text to display\r
55129      */\r
55130     setText : function(text){\r
55131         this.text = text||'&#160;';\r
55132         if(this.rendered){\r
55133             this.textEl.update(this.text);\r
55134             this.parentMenu.layout.doAutoSize();\r
55135         }\r
55136     },\r
55137 \r
55138     /**\r
55139      * Sets the CSS class to apply to the item's icon element\r
55140      * @param {String} cls The CSS class to apply\r
55141      */\r
55142     setIconClass : function(cls){\r
55143         var oldCls = this.iconCls;\r
55144         this.iconCls = cls;\r
55145         if(this.rendered){\r
55146             this.iconEl.replaceClass(oldCls, this.iconCls);\r
55147         }\r
55148     },\r
55149 \r
55150     //private\r
55151     beforeDestroy: function(){\r
55152         if (this.menu){\r
55153             delete this.menu.ownerCt;\r
55154             this.menu.destroy();\r
55155         }\r
55156         Ext.menu.Item.superclass.beforeDestroy.call(this);\r
55157     },\r
55158 \r
55159     // private\r
55160     handleClick : function(e){\r
55161         if(!this.href){ // if no link defined, stop the event automatically\r
55162             e.stopEvent();\r
55163         }\r
55164         Ext.menu.Item.superclass.handleClick.apply(this, arguments);\r
55165     },\r
55166 \r
55167     // private\r
55168     activate : function(autoExpand){\r
55169         if(Ext.menu.Item.superclass.activate.apply(this, arguments)){\r
55170             this.focus();\r
55171             if(autoExpand){\r
55172                 this.expandMenu();\r
55173             }\r
55174         }\r
55175         return true;\r
55176     },\r
55177 \r
55178     // private\r
55179     shouldDeactivate : function(e){\r
55180         if(Ext.menu.Item.superclass.shouldDeactivate.call(this, e)){\r
55181             if(this.menu && this.menu.isVisible()){\r
55182                 return !this.menu.getEl().getRegion().contains(e.getPoint());\r
55183             }\r
55184             return true;\r
55185         }\r
55186         return false;\r
55187     },\r
55188 \r
55189     // private\r
55190     deactivate : function(){\r
55191         Ext.menu.Item.superclass.deactivate.apply(this, arguments);\r
55192         this.hideMenu();\r
55193     },\r
55194 \r
55195     // private\r
55196     expandMenu : function(autoActivate){\r
55197         if(!this.disabled && this.menu){\r
55198             clearTimeout(this.hideTimer);\r
55199             delete this.hideTimer;\r
55200             if(!this.menu.isVisible() && !this.showTimer){\r
55201                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);\r
55202             }else if (this.menu.isVisible() && autoActivate){\r
55203                 this.menu.tryActivate(0, 1);\r
55204             }\r
55205         }\r
55206     },\r
55207 \r
55208     // private\r
55209     deferExpand : function(autoActivate){\r
55210         delete this.showTimer;\r
55211         this.menu.show(this.container, this.parentMenu.subMenuAlign || 'tl-tr?', this.parentMenu);\r
55212         if(autoActivate){\r
55213             this.menu.tryActivate(0, 1);\r
55214         }\r
55215     },\r
55216 \r
55217     // private\r
55218     hideMenu : function(){\r
55219         clearTimeout(this.showTimer);\r
55220         delete this.showTimer;\r
55221         if(!this.hideTimer && this.menu && this.menu.isVisible()){\r
55222             this.hideTimer = this.deferHide.defer(this.hideDelay, this);\r
55223         }\r
55224     },\r
55225 \r
55226     // private\r
55227     deferHide : function(){\r
55228         delete this.hideTimer;\r
55229         if(this.menu.over){\r
55230             this.parentMenu.setActiveItem(this, false);\r
55231         }else{\r
55232             this.menu.hide();\r
55233         }\r
55234     }\r
55235 });\r
55236 Ext.reg('menuitem', Ext.menu.Item);/**
55237  * @class Ext.menu.CheckItem
55238  * @extends Ext.menu.Item
55239  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
55240  * @constructor
55241  * Creates a new CheckItem
55242  * @param {Object} config Configuration options
55243  * @xtype menucheckitem
55244  */
55245 Ext.menu.CheckItem = Ext.extend(Ext.menu.Item, {
55246     /**
55247      * @cfg {String} group
55248      * All check items with the same group name will automatically be grouped into a single-select
55249      * radio button group (defaults to '')
55250      */
55251     /**
55252      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
55253      */
55254     itemCls : "x-menu-item x-menu-check-item",
55255     /**
55256      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
55257      */
55258     groupClass : "x-menu-group-item",
55259
55260     /**
55261      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
55262      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
55263      * initialized with checked = true will be rendered as checked.
55264      */
55265     checked: false,
55266
55267     // private
55268     ctype: "Ext.menu.CheckItem",
55269     
55270     initComponent : function(){
55271         Ext.menu.CheckItem.superclass.initComponent.call(this);
55272             this.addEvents(
55273                 /**
55274                  * @event beforecheckchange
55275                  * Fires before the checked value is set, providing an opportunity to cancel if needed
55276                  * @param {Ext.menu.CheckItem} this
55277                  * @param {Boolean} checked The new checked value that will be set
55278                  */
55279                 "beforecheckchange" ,
55280                 /**
55281                  * @event checkchange
55282                  * Fires after the checked value has been set
55283                  * @param {Ext.menu.CheckItem} this
55284                  * @param {Boolean} checked The checked value that was set
55285                  */
55286                 "checkchange"
55287             );
55288             /**
55289              * A function that handles the checkchange event.  The function is undefined by default, but if an implementation
55290              * is provided, it will be called automatically when the checkchange event fires.
55291              * @param {Ext.menu.CheckItem} this
55292              * @param {Boolean} checked The checked value that was set
55293              * @method checkHandler
55294              */
55295             if(this.checkHandler){
55296                 this.on('checkchange', this.checkHandler, this.scope);
55297             }
55298             Ext.menu.MenuMgr.registerCheckable(this);
55299     },
55300
55301     // private
55302     onRender : function(c){
55303         Ext.menu.CheckItem.superclass.onRender.apply(this, arguments);
55304         if(this.group){
55305             this.el.addClass(this.groupClass);
55306         }
55307         if(this.checked){
55308             this.checked = false;
55309             this.setChecked(true, true);
55310         }
55311     },
55312
55313     // private
55314     destroy : function(){
55315         Ext.menu.MenuMgr.unregisterCheckable(this);
55316         Ext.menu.CheckItem.superclass.destroy.apply(this, arguments);
55317     },
55318
55319     /**
55320      * Set the checked state of this item
55321      * @param {Boolean} checked The new checked value
55322      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
55323      */
55324     setChecked : function(state, suppressEvent){
55325         var suppress = suppressEvent === true;
55326         if(this.checked != state && (suppress || this.fireEvent("beforecheckchange", this, state) !== false)){
55327             if(this.container){
55328                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
55329             }
55330             this.checked = state;
55331             if(!suppress){
55332                 this.fireEvent("checkchange", this, state);
55333             }
55334         }
55335     },
55336
55337     // private
55338     handleClick : function(e){
55339        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
55340            this.setChecked(!this.checked);
55341        }
55342        Ext.menu.CheckItem.superclass.handleClick.apply(this, arguments);
55343     }
55344 });
55345 Ext.reg('menucheckitem', Ext.menu.CheckItem);/**\r
55346  * @class Ext.menu.DateMenu\r
55347  * @extends Ext.menu.Menu\r
55348  * <p>A menu containing an {@link Ext.DatePicker} Component.</p>\r
55349  * <p>Notes:</p><div class="mdetail-params"><ul>\r
55350  * <li>Although not listed here, the <b>constructor</b> for this class\r
55351  * accepts all of the configuration options of <b>{@link Ext.DatePicker}</b>.</li>\r
55352  * <li>If subclassing DateMenu, any configuration options for the DatePicker must be\r
55353  * applied to the <tt><b>initialConfig</b></tt> property of the DateMenu.\r
55354  * Applying {@link Ext.DatePicker DatePicker} configuration settings to\r
55355  * <b><tt>this</tt></b> will <b>not</b> affect the DatePicker's configuration.</li>\r
55356  * </ul></div>\r
55357  * @xtype datemenu\r
55358  */\r
55359  Ext.menu.DateMenu = Ext.extend(Ext.menu.Menu, {\r
55360     /** \r
55361      * @cfg {Boolean} enableScrolling\r
55362      * @hide \r
55363      */\r
55364     enableScrolling : false,\r
55365     /**\r
55366      * @cfg {Function} handler\r
55367      * Optional. A function that will handle the select event of this menu.\r
55368      * The handler is passed the following parameters:<div class="mdetail-params"><ul>\r
55369      * <li><code>picker</code> : DatePicker<div class="sub-desc">The Ext.DatePicker.</div></li>\r
55370      * <li><code>date</code> : Date<div class="sub-desc">The selected date.</div></li>\r
55371      * </ul></div>\r
55372      */\r
55373     /**\r
55374      * @cfg {Object} scope\r
55375      * The scope (<tt><b>this</b></tt> reference) in which the <code>{@link #handler}</code>\r
55376      * function will be called.  Defaults to this DateMenu instance.\r
55377      */    \r
55378     /** \r
55379      * @cfg {Boolean} hideOnClick\r
55380      * False to continue showing the menu after a date is selected, defaults to true.\r
55381      */\r
55382     hideOnClick : true,\r
55383     \r
55384     /** \r
55385      * @cfg {String} pickerId\r
55386      * An id to assign to the underlying date picker. Defaults to <tt>null</tt>.\r
55387      */\r
55388     pickerId : null,\r
55389     \r
55390     /** \r
55391      * @cfg {Number} maxHeight\r
55392      * @hide \r
55393      */\r
55394     /** \r
55395      * @cfg {Number} scrollIncrement\r
55396      * @hide \r
55397      */\r
55398     /**\r
55399      * The {@link Ext.DatePicker} instance for this DateMenu\r
55400      * @property picker\r
55401      * @type DatePicker\r
55402      */\r
55403     cls : 'x-date-menu',\r
55404     \r
55405     /**\r
55406      * @event click\r
55407      * @hide\r
55408      */\r
55409     \r
55410     /**\r
55411      * @event itemclick\r
55412      * @hide\r
55413      */\r
55414 \r
55415     initComponent : function(){\r
55416         this.on('beforeshow', this.onBeforeShow, this);\r
55417         if(this.strict = (Ext.isIE7 && Ext.isStrict)){\r
55418             this.on('show', this.onShow, this, {single: true, delay: 20});\r
55419         }\r
55420         Ext.apply(this, {\r
55421             plain: true,\r
55422             showSeparator: false,\r
55423             items: this.picker = new Ext.DatePicker(Ext.applyIf({\r
55424                 internalRender: this.strict || !Ext.isIE,\r
55425                 ctCls: 'x-menu-date-item',\r
55426                 id: this.pickerId\r
55427             }, this.initialConfig))\r
55428         });\r
55429         this.picker.purgeListeners();\r
55430         Ext.menu.DateMenu.superclass.initComponent.call(this);\r
55431         /**\r
55432          * @event select\r
55433          * Fires when a date is selected from the {@link #picker Ext.DatePicker}\r
55434          * @param {DatePicker} picker The {@link #picker Ext.DatePicker}\r
55435          * @param {Date} date The selected date\r
55436          */\r
55437         this.relayEvents(this.picker, ['select']);\r
55438         this.on('show', this.picker.focus, this.picker);\r
55439         this.on('select', this.menuHide, this);\r
55440         if(this.handler){\r
55441             this.on('select', this.handler, this.scope || this);\r
55442         }\r
55443     },\r
55444 \r
55445     menuHide : function() {\r
55446         if(this.hideOnClick){\r
55447             this.hide(true);\r
55448         }\r
55449     },\r
55450 \r
55451     onBeforeShow : function(){\r
55452         if(this.picker){\r
55453             this.picker.hideMonthPicker(true);\r
55454         }\r
55455     },\r
55456 \r
55457     onShow : function(){\r
55458         var el = this.picker.getEl();\r
55459         el.setWidth(el.getWidth()); //nasty hack for IE7 strict mode\r
55460     }\r
55461  });\r
55462  Ext.reg('datemenu', Ext.menu.DateMenu);\r
55463  /**\r
55464  * @class Ext.menu.ColorMenu\r
55465  * @extends Ext.menu.Menu\r
55466  * <p>A menu containing a {@link Ext.ColorPalette} Component.</p>\r
55467  * <p>Notes:</p><div class="mdetail-params"><ul>\r
55468  * <li>Although not listed here, the <b>constructor</b> for this class\r
55469  * accepts all of the configuration options of <b>{@link Ext.ColorPalette}</b>.</li>\r
55470  * <li>If subclassing ColorMenu, any configuration options for the ColorPalette must be\r
55471  * applied to the <tt><b>initialConfig</b></tt> property of the ColorMenu.\r
55472  * Applying {@link Ext.ColorPalette ColorPalette} configuration settings to\r
55473  * <b><tt>this</tt></b> will <b>not</b> affect the ColorPalette's configuration.</li>\r
55474  * </ul></div> * \r
55475  * @xtype colormenu\r
55476  */\r
55477  Ext.menu.ColorMenu = Ext.extend(Ext.menu.Menu, {\r
55478     /** \r
55479      * @cfg {Boolean} enableScrolling\r
55480      * @hide \r
55481      */\r
55482     enableScrolling : false,\r
55483     /**\r
55484      * @cfg {Function} handler\r
55485      * Optional. A function that will handle the select event of this menu.\r
55486      * The handler is passed the following parameters:<div class="mdetail-params"><ul>\r
55487      * <li><code>palette</code> : ColorPalette<div class="sub-desc">The {@link #palette Ext.ColorPalette}.</div></li>\r
55488      * <li><code>color</code> : String<div class="sub-desc">The 6-digit color hex code (without the # symbol).</div></li>\r
55489      * </ul></div>\r
55490      */\r
55491     /**\r
55492      * @cfg {Object} scope\r
55493      * The scope (<tt><b>this</b></tt> reference) in which the <code>{@link #handler}</code>\r
55494      * function will be called.  Defaults to this ColorMenu instance.\r
55495      */    \r
55496     \r
55497     /** \r
55498      * @cfg {Boolean} hideOnClick\r
55499      * False to continue showing the menu after a color is selected, defaults to true.\r
55500      */\r
55501     hideOnClick : true,\r
55502     \r
55503     cls : 'x-color-menu',\r
55504     \r
55505     /** \r
55506      * @cfg {String} paletteId\r
55507      * An id to assign to the underlying color palette. Defaults to <tt>null</tt>.\r
55508      */\r
55509     paletteId : null,\r
55510     \r
55511     /** \r
55512      * @cfg {Number} maxHeight\r
55513      * @hide \r
55514      */\r
55515     /** \r
55516      * @cfg {Number} scrollIncrement\r
55517      * @hide \r
55518      */\r
55519     /**\r
55520      * @property palette\r
55521      * @type ColorPalette\r
55522      * The {@link Ext.ColorPalette} instance for this ColorMenu\r
55523      */\r
55524     \r
55525     \r
55526     /**\r
55527      * @event click\r
55528      * @hide\r
55529      */\r
55530     \r
55531     /**\r
55532      * @event itemclick\r
55533      * @hide\r
55534      */\r
55535     \r
55536     initComponent : function(){\r
55537         Ext.apply(this, {\r
55538             plain: true,\r
55539             showSeparator: false,\r
55540             items: this.palette = new Ext.ColorPalette(Ext.applyIf({\r
55541                 id: this.paletteId\r
55542             }, this.initialConfig))\r
55543         });\r
55544         this.palette.purgeListeners();\r
55545         Ext.menu.ColorMenu.superclass.initComponent.call(this);\r
55546         /**\r
55547          * @event select\r
55548          * Fires when a color is selected from the {@link #palette Ext.ColorPalette}\r
55549          * @param {Ext.ColorPalette} palette The {@link #palette Ext.ColorPalette}\r
55550              * @param {String} color The 6-digit color hex code (without the # symbol)\r
55551          */\r
55552         this.relayEvents(this.palette, ['select']);\r
55553         this.on('select', this.menuHide, this);\r
55554         if(this.handler){\r
55555             this.on('select', this.handler, this.scope || this);\r
55556         }\r
55557     },\r
55558 \r
55559     menuHide : function(){\r
55560         if(this.hideOnClick){\r
55561             this.hide(true);\r
55562         }\r
55563     }\r
55564 });\r
55565 Ext.reg('colormenu', Ext.menu.ColorMenu);\r
55566 /**
55567  * @class Ext.form.Field
55568  * @extends Ext.BoxComponent
55569  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
55570  * @constructor
55571  * Creates a new Field
55572  * @param {Object} config Configuration options
55573  * @xtype field
55574  */
55575 Ext.form.Field = Ext.extend(Ext.BoxComponent,  {
55576     /**
55577      * <p>The label Element associated with this Field. <b>Only available after this Field has been rendered by a
55578      * {@link form Ext.layout.FormLayout} layout manager.</b></p>
55579      * @type Ext.Element
55580      * @property label
55581      */
55582     /**
55583      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password, file (defaults
55584      * to 'text'). The types 'file' and 'password' must be used to render those field types currently -- there are
55585      * no separate Ext components for those. Note that if you use <tt>inputType:'file'</tt>, {@link #emptyText}
55586      * is not supported and should be avoided.
55587      */
55588     /**
55589      * @cfg {Number} tabIndex The tabIndex for this field. Note this only applies to fields that are rendered,
55590      * not those which are built via applyTo (defaults to undefined).
55591      */
55592     /**
55593      * @cfg {Mixed} value A value to initialize this field with (defaults to undefined).
55594      */
55595     /**
55596      * @cfg {String} name The field's HTML name attribute (defaults to '').
55597      * <b>Note</b>: this property must be set if this field is to be automatically included with
55598      * {@link Ext.form.BasicForm#submit form submit()}.
55599      */
55600     /**
55601      * @cfg {String} cls A custom CSS class to apply to the field's underlying element (defaults to '').
55602      */
55603
55604     /**
55605      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to 'x-form-invalid')
55606      */
55607     invalidClass : 'x-form-invalid',
55608     /**
55609      * @cfg {String} invalidText The error text to use when marking a field invalid and no message is provided
55610      * (defaults to 'The value in this field is invalid')
55611      */
55612     invalidText : 'The value in this field is invalid',
55613     /**
55614      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to 'x-form-focus')
55615      */
55616     focusClass : 'x-form-focus',
55617     /**
55618      * @cfg {Boolean} preventMark
55619      * <tt>true</tt> to disable {@link #markInvalid marking the field invalid}.
55620      * Defaults to <tt>false</tt>.
55621      */
55622     /**
55623      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
55624       automatic validation (defaults to 'keyup').
55625      */
55626     validationEvent : 'keyup',
55627     /**
55628      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
55629      */
55630     validateOnBlur : true,
55631     /**
55632      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation
55633      * is initiated (defaults to 250)
55634      */
55635     validationDelay : 250,
55636     /**
55637      * @cfg {String/Object} autoCreate <p>A {@link Ext.DomHelper DomHelper} element spec, or true for a default
55638      * element spec. Used to create the {@link Ext.Component#getEl Element} which will encapsulate this Component.
55639      * See <tt>{@link Ext.Component#autoEl autoEl}</tt> for details.  Defaults to:</p>
55640      * <pre><code>{tag: 'input', type: 'text', size: '20', autocomplete: 'off'}</code></pre>
55641      */
55642     defaultAutoCreate : {tag: 'input', type: 'text', size: '20', autocomplete: 'off'},
55643     /**
55644      * @cfg {String} fieldClass The default CSS class for the field (defaults to 'x-form-field')
55645      */
55646     fieldClass : 'x-form-field',
55647     /**
55648      * @cfg {String} msgTarget<p>The location where the message text set through {@link #markInvalid} should display.
55649      * Must be one of the following values:</p>
55650      * <div class="mdetail-params"><ul>
55651      * <li><code>qtip</code> Display a quick tip containing the message when the user hovers over the field. This is the default.
55652      * <div class="subdesc"><b>{@link Ext.QuickTips#init Ext.QuickTips.init} must have been called for this setting to work.</b></div</li>
55653      * <li><code>title</code> Display the message in a default browser title attribute popup.</li>
55654      * <li><code>under</code> Add a block div beneath the field containing the error message.</li>
55655      * <li><code>side</code> Add an error icon to the right of the field, displaying the message in a popup on hover.</li>
55656      * <li><code>[element id]</code> Add the error message directly to the innerHTML of the specified element.</li>
55657      * </ul></div>
55658      */
55659     msgTarget : 'qtip',
55660     /**
55661      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field
55662      * (defaults to 'normal').
55663      */
55664     msgFx : 'normal',
55665     /**
55666      * @cfg {Boolean} readOnly <tt>true</tt> to mark the field as readOnly in HTML
55667      * (defaults to <tt>false</tt>).
55668      * <br><p><b>Note</b>: this only sets the element's readOnly DOM attribute.
55669      * Setting <code>readOnly=true</code>, for example, will not disable triggering a
55670      * ComboBox or DateField; it gives you the option of forcing the user to choose
55671      * via the trigger without typing in the text box. To hide the trigger use
55672      * <code>{@link Ext.form.TriggerField#hideTrigger hideTrigger}</code>.</p>
55673      */
55674     readOnly : false,
55675     /**
55676      * @cfg {Boolean} disabled True to disable the field (defaults to false).
55677      * <p>Be aware that conformant with the <a href="http://www.w3.org/TR/html401/interact/forms.html#h-17.12.1">HTML specification</a>,
55678      * disabled Fields will not be {@link Ext.form.BasicForm#submit submitted}.</p>
55679      */
55680     disabled : false,
55681     /**
55682      * @cfg {Boolean} submitValue False to clear the name attribute on the field so that it is not submitted during a form post.
55683      * Defaults to <tt>true</tt>.
55684      */
55685     submitValue: true,
55686
55687     // private
55688     isFormField : true,
55689
55690     // private
55691     msgDisplay: '',
55692
55693     // private
55694     hasFocus : false,
55695
55696     // private
55697     initComponent : function(){
55698         Ext.form.Field.superclass.initComponent.call(this);
55699         this.addEvents(
55700             /**
55701              * @event focus
55702              * Fires when this field receives input focus.
55703              * @param {Ext.form.Field} this
55704              */
55705             'focus',
55706             /**
55707              * @event blur
55708              * Fires when this field loses input focus.
55709              * @param {Ext.form.Field} this
55710              */
55711             'blur',
55712             /**
55713              * @event specialkey
55714              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.
55715              * To handle other keys see {@link Ext.Panel#keys} or {@link Ext.KeyMap}.
55716              * You can check {@link Ext.EventObject#getKey} to determine which key was pressed.
55717              * For example: <pre><code>
55718 var form = new Ext.form.FormPanel({
55719     ...
55720     items: [{
55721             fieldLabel: 'Field 1',
55722             name: 'field1',
55723             allowBlank: false
55724         },{
55725             fieldLabel: 'Field 2',
55726             name: 'field2',
55727             listeners: {
55728                 specialkey: function(field, e){
55729                     // e.HOME, e.END, e.PAGE_UP, e.PAGE_DOWN,
55730                     // e.TAB, e.ESC, arrow keys: e.LEFT, e.RIGHT, e.UP, e.DOWN
55731                     if (e.{@link Ext.EventObject#getKey getKey()} == e.ENTER) {
55732                         var form = field.ownerCt.getForm();
55733                         form.submit();
55734                     }
55735                 }
55736             }
55737         }
55738     ],
55739     ...
55740 });
55741              * </code></pre>
55742              * @param {Ext.form.Field} this
55743              * @param {Ext.EventObject} e The event object
55744              */
55745             'specialkey',
55746             /**
55747              * @event change
55748              * Fires just before the field blurs if the field value has changed.
55749              * @param {Ext.form.Field} this
55750              * @param {Mixed} newValue The new value
55751              * @param {Mixed} oldValue The original value
55752              */
55753             'change',
55754             /**
55755              * @event invalid
55756              * Fires after the field has been marked as invalid.
55757              * @param {Ext.form.Field} this
55758              * @param {String} msg The validation message
55759              */
55760             'invalid',
55761             /**
55762              * @event valid
55763              * Fires after the field has been validated with no errors.
55764              * @param {Ext.form.Field} this
55765              */
55766             'valid'
55767         );
55768     },
55769
55770     /**
55771      * Returns the {@link Ext.form.Field#name name} or {@link Ext.form.ComboBox#hiddenName hiddenName}
55772      * attribute of the field if available.
55773      * @return {String} name The field {@link Ext.form.Field#name name} or {@link Ext.form.ComboBox#hiddenName hiddenName}
55774      */
55775     getName : function(){
55776         return this.rendered && this.el.dom.name ? this.el.dom.name : this.name || this.id || '';
55777     },
55778
55779     // private
55780     onRender : function(ct, position){
55781         if(!this.el){
55782             var cfg = this.getAutoCreate();
55783
55784             if(!cfg.name){
55785                 cfg.name = this.name || this.id;
55786             }
55787             if(this.inputType){
55788                 cfg.type = this.inputType;
55789             }
55790             this.autoEl = cfg;
55791         }
55792         Ext.form.Field.superclass.onRender.call(this, ct, position);
55793         if(this.submitValue === false){
55794             this.el.dom.removeAttribute('name');
55795         }
55796         var type = this.el.dom.type;
55797         if(type){
55798             if(type == 'password'){
55799                 type = 'text';
55800             }
55801             this.el.addClass('x-form-'+type);
55802         }
55803         if(this.readOnly){
55804             this.setReadOnly(true);
55805         }
55806         if(this.tabIndex !== undefined){
55807             this.el.dom.setAttribute('tabIndex', this.tabIndex);
55808         }
55809
55810         this.el.addClass([this.fieldClass, this.cls]);
55811     },
55812
55813     // private
55814     getItemCt : function(){
55815         return this.itemCt;
55816     },
55817
55818     // private
55819     initValue : function(){
55820         if(this.value !== undefined){
55821             this.setValue(this.value);
55822         }else if(!Ext.isEmpty(this.el.dom.value) && this.el.dom.value != this.emptyText){
55823             this.setValue(this.el.dom.value);
55824         }
55825         /**
55826          * The original value of the field as configured in the {@link #value} configuration, or
55827          * as loaded by the last form load operation if the form's {@link Ext.form.BasicForm#trackResetOnLoad trackResetOnLoad}
55828          * setting is <code>true</code>.
55829          * @type mixed
55830          * @property originalValue
55831          */
55832         this.originalValue = this.getValue();
55833     },
55834
55835     /**
55836      * <p>Returns true if the value of this Field has been changed from its original value.
55837      * Will return false if the field is disabled or has not been rendered yet.</p>
55838      * <p>Note that if the owning {@link Ext.form.BasicForm form} was configured with
55839      * {@link Ext.form.BasicForm}.{@link Ext.form.BasicForm#trackResetOnLoad trackResetOnLoad}
55840      * then the <i>original value</i> is updated when the values are loaded by
55841      * {@link Ext.form.BasicForm}.{@link Ext.form.BasicForm#setValues setValues}.</p>
55842      * @return {Boolean} True if this field has been changed from its original value (and
55843      * is not disabled), false otherwise.
55844      */
55845     isDirty : function() {
55846         if(this.disabled || !this.rendered) {
55847             return false;
55848         }
55849         return String(this.getValue()) !== String(this.originalValue);
55850     },
55851
55852     /**
55853      * Sets the read only state of this field.
55854      * @param {Boolean} readOnly Whether the field should be read only.
55855      */
55856     setReadOnly : function(readOnly){
55857         if(this.rendered){
55858             this.el.dom.readOnly = readOnly;
55859         }
55860         this.readOnly = readOnly;
55861     },
55862
55863     // private
55864     afterRender : function(){
55865         Ext.form.Field.superclass.afterRender.call(this);
55866         this.initEvents();
55867         this.initValue();
55868     },
55869
55870     // private
55871     fireKey : function(e){
55872         if(e.isSpecialKey()){
55873             this.fireEvent('specialkey', this, e);
55874         }
55875     },
55876
55877     /**
55878      * Resets the current field value to the originally loaded value and clears any validation messages.
55879      * See {@link Ext.form.BasicForm}.{@link Ext.form.BasicForm#trackResetOnLoad trackResetOnLoad}
55880      */
55881     reset : function(){
55882         this.setValue(this.originalValue);
55883         this.clearInvalid();
55884     },
55885
55886     // private
55887     initEvents : function(){
55888         this.mon(this.el, Ext.EventManager.useKeydown ? 'keydown' : 'keypress', this.fireKey,  this);
55889         this.mon(this.el, 'focus', this.onFocus, this);
55890
55891         // standardise buffer across all browsers + OS-es for consistent event order.
55892         // (the 10ms buffer for Editors fixes a weird FF/Win editor issue when changing OS window focus)
55893         this.mon(this.el, 'blur', this.onBlur, this, this.inEditor ? {buffer:10} : null);
55894     },
55895
55896     // private
55897     preFocus: Ext.emptyFn,
55898
55899     // private
55900     onFocus : function(){
55901         this.preFocus();
55902         if(this.focusClass){
55903             this.el.addClass(this.focusClass);
55904         }
55905         if(!this.hasFocus){
55906             this.hasFocus = true;
55907             /**
55908              * <p>The value that the Field had at the time it was last focused. This is the value that is passed
55909              * to the {@link #change} event which is fired if the value has been changed when the Field is blurred.</p>
55910              * <p><b>This will be undefined until the Field has been visited.</b> Compare {@link #originalValue}.</p>
55911              * @type mixed
55912              * @property startValue
55913              */
55914             this.startValue = this.getValue();
55915             this.fireEvent('focus', this);
55916         }
55917     },
55918
55919     // private
55920     beforeBlur : Ext.emptyFn,
55921
55922     // private
55923     onBlur : function(){
55924         this.beforeBlur();
55925         if(this.focusClass){
55926             this.el.removeClass(this.focusClass);
55927         }
55928         this.hasFocus = false;
55929         if(this.validationEvent !== false && (this.validateOnBlur || this.validationEvent == 'blur')){
55930             this.validate();
55931         }
55932         var v = this.getValue();
55933         if(String(v) !== String(this.startValue)){
55934             this.fireEvent('change', this, v, this.startValue);
55935         }
55936         this.fireEvent('blur', this);
55937         this.postBlur();
55938     },
55939
55940     // private
55941     postBlur : Ext.emptyFn,
55942
55943     /**
55944      * Returns whether or not the field value is currently valid by
55945      * {@link #validateValue validating} the {@link #processValue processed value}
55946      * of the field. <b>Note</b>: {@link #disabled} fields are ignored.
55947      * @param {Boolean} preventMark True to disable marking the field invalid
55948      * @return {Boolean} True if the value is valid, else false
55949      */
55950     isValid : function(preventMark){
55951         if(this.disabled){
55952             return true;
55953         }
55954         var restore = this.preventMark;
55955         this.preventMark = preventMark === true;
55956         var v = this.validateValue(this.processValue(this.getRawValue()));
55957         this.preventMark = restore;
55958         return v;
55959     },
55960
55961     /**
55962      * Validates the field value
55963      * @return {Boolean} True if the value is valid, else false
55964      */
55965     validate : function(){
55966         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
55967             this.clearInvalid();
55968             return true;
55969         }
55970         return false;
55971     },
55972
55973     /**
55974      * This method should only be overridden if necessary to prepare raw values
55975      * for validation (see {@link #validate} and {@link #isValid}).  This method
55976      * is expected to return the processed value for the field which will
55977      * be used for validation (see validateValue method).
55978      * @param {Mixed} value
55979      */
55980     processValue : function(value){
55981         return value;
55982     },
55983
55984     /**
55985      * @private
55986      * Subclasses should provide the validation implementation by overriding this
55987      * @param {Mixed} value
55988      */
55989     validateValue : function(value){
55990         return true;
55991     },
55992
55993     /**
55994      * Gets the active error message for this field.
55995      * @return {String} Returns the active error message on the field, if there is no error, an empty string is returned.
55996      */
55997     getActiveError : function(){
55998         return this.activeError || '';
55999     },
56000
56001     /**
56002      * <p>Display an error message associated with this field, using {@link #msgTarget} to determine how to
56003      * display the message and applying {@link #invalidClass} to the field's UI element.</p>
56004      * <p><b>Note</b>: this method does not cause the Field's {@link #validate} method to return <code>false</code>
56005      * if the value does <i>pass</i> validation. So simply marking a Field as invalid will not prevent
56006      * submission of forms submitted with the {@link Ext.form.Action.Submit#clientValidation} option set.</p>
56007      * {@link #isValid invalid}.
56008      * @param {String} msg (optional) The validation message (defaults to {@link #invalidText})
56009      */
56010     markInvalid : function(msg){
56011         if(!this.rendered || this.preventMark){ // not rendered
56012             return;
56013         }
56014         msg = msg || this.invalidText;
56015
56016         var mt = this.getMessageHandler();
56017         if(mt){
56018             mt.mark(this, msg);
56019         }else if(this.msgTarget){
56020             this.el.addClass(this.invalidClass);
56021             var t = Ext.getDom(this.msgTarget);
56022             if(t){
56023                 t.innerHTML = msg;
56024                 t.style.display = this.msgDisplay;
56025             }
56026         }
56027         this.activeError = msg;
56028         this.fireEvent('invalid', this, msg);
56029     },
56030
56031     /**
56032      * Clear any invalid styles/messages for this field
56033      */
56034     clearInvalid : function(){
56035         if(!this.rendered || this.preventMark){ // not rendered
56036             return;
56037         }
56038         this.el.removeClass(this.invalidClass);
56039         var mt = this.getMessageHandler();
56040         if(mt){
56041             mt.clear(this);
56042         }else if(this.msgTarget){
56043             this.el.removeClass(this.invalidClass);
56044             var t = Ext.getDom(this.msgTarget);
56045             if(t){
56046                 t.innerHTML = '';
56047                 t.style.display = 'none';
56048             }
56049         }
56050         delete this.activeError;
56051         this.fireEvent('valid', this);
56052     },
56053
56054     // private
56055     getMessageHandler : function(){
56056         return Ext.form.MessageTargets[this.msgTarget];
56057     },
56058
56059     // private
56060     getErrorCt : function(){
56061         return this.el.findParent('.x-form-element', 5, true) || // use form element wrap if available
56062             this.el.findParent('.x-form-field-wrap', 5, true);   // else direct field wrap
56063     },
56064
56065     // Alignment for 'under' target
56066     alignErrorEl : function(){
56067         this.errorEl.setWidth(this.getErrorCt().getWidth(true) - 20);
56068     },
56069
56070     // Alignment for 'side' target
56071     alignErrorIcon : function(){
56072         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
56073     },
56074
56075     /**
56076      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
56077      * @return {Mixed} value The field value
56078      */
56079     getRawValue : function(){
56080         var v = this.rendered ? this.el.getValue() : Ext.value(this.value, '');
56081         if(v === this.emptyText){
56082             v = '';
56083         }
56084         return v;
56085     },
56086
56087     /**
56088      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
56089      * @return {Mixed} value The field value
56090      */
56091     getValue : function(){
56092         if(!this.rendered) {
56093             return this.value;
56094         }
56095         var v = this.el.getValue();
56096         if(v === this.emptyText || v === undefined){
56097             v = '';
56098         }
56099         return v;
56100     },
56101
56102     /**
56103      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
56104      * @param {Mixed} value The value to set
56105      * @return {Mixed} value The field value that is set
56106      */
56107     setRawValue : function(v){
56108         return this.rendered ? (this.el.dom.value = (Ext.isEmpty(v) ? '' : v)) : '';
56109     },
56110
56111     /**
56112      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
56113      * @param {Mixed} value The value to set
56114      * @return {Ext.form.Field} this
56115      */
56116     setValue : function(v){
56117         this.value = v;
56118         if(this.rendered){
56119             this.el.dom.value = (Ext.isEmpty(v) ? '' : v);
56120             this.validate();
56121         }
56122         return this;
56123     },
56124
56125     // private, does not work for all fields
56126     append : function(v){
56127          this.setValue([this.getValue(), v].join(''));
56128     }
56129
56130     /**
56131      * @cfg {Boolean} autoWidth @hide
56132      */
56133     /**
56134      * @cfg {Boolean} autoHeight @hide
56135      */
56136
56137     /**
56138      * @cfg {String} autoEl @hide
56139      */
56140 });
56141
56142
56143 Ext.form.MessageTargets = {
56144     'qtip' : {
56145         mark: function(field, msg){
56146             field.el.addClass(field.invalidClass);
56147             field.el.dom.qtip = msg;
56148             field.el.dom.qclass = 'x-form-invalid-tip';
56149             if(Ext.QuickTips){ // fix for floating editors interacting with DND
56150                 Ext.QuickTips.enable();
56151             }
56152         },
56153         clear: function(field){
56154             field.el.removeClass(field.invalidClass);
56155             field.el.dom.qtip = '';
56156         }
56157     },
56158     'title' : {
56159         mark: function(field, msg){
56160             field.el.addClass(field.invalidClass);
56161             field.el.dom.title = msg;
56162         },
56163         clear: function(field){
56164             field.el.dom.title = '';
56165         }
56166     },
56167     'under' : {
56168         mark: function(field, msg){
56169             field.el.addClass(field.invalidClass);
56170             if(!field.errorEl){
56171                 var elp = field.getErrorCt();
56172                 if(!elp){ // field has no container el
56173                     field.el.dom.title = msg;
56174                     return;
56175                 }
56176                 field.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
56177                 field.on('resize', field.alignErrorEl, field);
56178                 field.on('destroy', function(){
56179                     Ext.destroy(this.errorEl);
56180                 }, field);
56181             }
56182             field.alignErrorEl();
56183             field.errorEl.update(msg);
56184             Ext.form.Field.msgFx[field.msgFx].show(field.errorEl, field);
56185         },
56186         clear: function(field){
56187             field.el.removeClass(field.invalidClass);
56188             if(field.errorEl){
56189                 Ext.form.Field.msgFx[field.msgFx].hide(field.errorEl, field);
56190             }else{
56191                 field.el.dom.title = '';
56192             }
56193         }
56194     },
56195     'side' : {
56196         mark: function(field, msg){
56197             field.el.addClass(field.invalidClass);
56198             if(!field.errorIcon){
56199                 var elp = field.getErrorCt();
56200                 if(!elp){ // field has no container el
56201                     field.el.dom.title = msg;
56202                     return;
56203                 }
56204                 field.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
56205                 field.on('resize', field.alignErrorIcon, field);
56206                 field.on('destroy', function(){
56207                     Ext.destroy(this.errorIcon);
56208                 }, field);
56209             }
56210             field.alignErrorIcon();
56211             field.errorIcon.dom.qtip = msg;
56212             field.errorIcon.dom.qclass = 'x-form-invalid-tip';
56213             field.errorIcon.show();
56214         },
56215         clear: function(field){
56216             field.el.removeClass(field.invalidClass);
56217             if(field.errorIcon){
56218                 field.errorIcon.dom.qtip = '';
56219                 field.errorIcon.hide();
56220             }else{
56221                 field.el.dom.title = '';
56222             }
56223         }
56224     }
56225 };
56226
56227 // anything other than normal should be considered experimental
56228 Ext.form.Field.msgFx = {
56229     normal : {
56230         show: function(msgEl, f){
56231             msgEl.setDisplayed('block');
56232         },
56233
56234         hide : function(msgEl, f){
56235             msgEl.setDisplayed(false).update('');
56236         }
56237     },
56238
56239     slide : {
56240         show: function(msgEl, f){
56241             msgEl.slideIn('t', {stopFx:true});
56242         },
56243
56244         hide : function(msgEl, f){
56245             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
56246         }
56247     },
56248
56249     slideRight : {
56250         show: function(msgEl, f){
56251             msgEl.fixDisplay();
56252             msgEl.alignTo(f.el, 'tl-tr');
56253             msgEl.slideIn('l', {stopFx:true});
56254         },
56255
56256         hide : function(msgEl, f){
56257             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
56258         }
56259     }
56260 };
56261 Ext.reg('field', Ext.form.Field);
56262 /**
56263  * @class Ext.form.TextField
56264  * @extends Ext.form.Field
56265  * <p>Basic text field.  Can be used as a direct replacement for traditional text inputs,
56266  * or as the base class for more sophisticated input controls (like {@link Ext.form.TextArea}
56267  * and {@link Ext.form.ComboBox}).</p>
56268  * <p><b><u>Validation</u></b></p>
56269  * <p>The validation procedure is described in the documentation for {@link #validateValue}.</p>
56270  * <p><b><u>Alter Validation Behavior</u></b></p>
56271  * <p>Validation behavior for each field can be configured:</p>
56272  * <div class="mdetail-params"><ul>
56273  * <li><code>{@link Ext.form.TextField#invalidText invalidText}</code> : the default validation message to
56274  * show if any validation step above does not provide a message when invalid</li>
56275  * <li><code>{@link Ext.form.TextField#maskRe maskRe}</code> : filter out keystrokes before any validation occurs</li>
56276  * <li><code>{@link Ext.form.TextField#stripCharsRe stripCharsRe}</code> : filter characters after being typed in,
56277  * but before being validated</li>
56278  * <li><code>{@link Ext.form.Field#invalidClass invalidClass}</code> : alternate style when invalid</li>
56279  * <li><code>{@link Ext.form.Field#validateOnBlur validateOnBlur}</code>,
56280  * <code>{@link Ext.form.Field#validationDelay validationDelay}</code>, and
56281  * <code>{@link Ext.form.Field#validationEvent validationEvent}</code> : modify how/when validation is triggered</li>
56282  * </ul></div>
56283  * 
56284  * @constructor Creates a new TextField
56285  * @param {Object} config Configuration options
56286  * 
56287  * @xtype textfield
56288  */
56289 Ext.form.TextField = Ext.extend(Ext.form.Field,  {
56290     /**
56291      * @cfg {String} vtypeText A custom error message to display in place of the default message provided
56292      * for the <b><code>{@link #vtype}</code></b> currently set for this field (defaults to <tt>''</tt>).  <b>Note</b>:
56293      * only applies if <b><code>{@link #vtype}</code></b> is set, else ignored.
56294      */
56295     /**
56296      * @cfg {RegExp} stripCharsRe A JavaScript RegExp object used to strip unwanted content from the value
56297      * before validation (defaults to <tt>null</tt>).
56298      */
56299     /**
56300      * @cfg {Boolean} grow <tt>true</tt> if this field should automatically grow and shrink to its content
56301      * (defaults to <tt>false</tt>)
56302      */
56303     grow : false,
56304     /**
56305      * @cfg {Number} growMin The minimum width to allow when <code><b>{@link #grow}</b> = true</code> (defaults
56306      * to <tt>30</tt>)
56307      */
56308     growMin : 30,
56309     /**
56310      * @cfg {Number} growMax The maximum width to allow when <code><b>{@link #grow}</b> = true</code> (defaults
56311      * to <tt>800</tt>)
56312      */
56313     growMax : 800,
56314     /**
56315      * @cfg {String} vtype A validation type name as defined in {@link Ext.form.VTypes} (defaults to <tt>null</tt>)
56316      */
56317     vtype : null,
56318     /**
56319      * @cfg {RegExp} maskRe An input mask regular expression that will be used to filter keystrokes that do
56320      * not match (defaults to <tt>null</tt>)
56321      */
56322     maskRe : null,
56323     /**
56324      * @cfg {Boolean} disableKeyFilter Specify <tt>true</tt> to disable input keystroke filtering (defaults
56325      * to <tt>false</tt>)
56326      */
56327     disableKeyFilter : false,
56328     /**
56329      * @cfg {Boolean} allowBlank Specify <tt>false</tt> to validate that the value's length is > 0 (defaults to
56330      * <tt>true</tt>)
56331      */
56332     allowBlank : true,
56333     /**
56334      * @cfg {Number} minLength Minimum input field length required (defaults to <tt>0</tt>)
56335      */
56336     minLength : 0,
56337     /**
56338      * @cfg {Number} maxLength Maximum input field length allowed by validation (defaults to Number.MAX_VALUE).
56339      * This behavior is intended to provide instant feedback to the user by improving usability to allow pasting
56340      * and editing or overtyping and back tracking. To restrict the maximum number of characters that can be
56341      * entered into the field use <tt><b>{@link Ext.form.Field#autoCreate autoCreate}</b></tt> to add
56342      * any attributes you want to a field, for example:<pre><code>
56343 var myField = new Ext.form.NumberField({
56344     id: 'mobile',
56345     anchor:'90%',
56346     fieldLabel: 'Mobile',
56347     maxLength: 16, // for validation
56348     autoCreate: {tag: 'input', type: 'text', size: '20', autocomplete: 'off', maxlength: '10'}
56349 });
56350 </code></pre>
56351      */
56352     maxLength : Number.MAX_VALUE,
56353     /**
56354      * @cfg {String} minLengthText Error text to display if the <b><tt>{@link #minLength minimum length}</tt></b>
56355      * validation fails (defaults to <tt>'The minimum length for this field is {minLength}'</tt>)
56356      */
56357     minLengthText : 'The minimum length for this field is {0}',
56358     /**
56359      * @cfg {String} maxLengthText Error text to display if the <b><tt>{@link #maxLength maximum length}</tt></b>
56360      * validation fails (defaults to <tt>'The maximum length for this field is {maxLength}'</tt>)
56361      */
56362     maxLengthText : 'The maximum length for this field is {0}',
56363     /**
56364      * @cfg {Boolean} selectOnFocus <tt>true</tt> to automatically select any existing field text when the field
56365      * receives input focus (defaults to <tt>false</tt>)
56366      */
56367     selectOnFocus : false,
56368     /**
56369      * @cfg {String} blankText The error text to display if the <b><tt>{@link #allowBlank}</tt></b> validation
56370      * fails (defaults to <tt>'This field is required'</tt>)
56371      */
56372     blankText : 'This field is required',
56373     /**
56374      * @cfg {Function} validator
56375      * <p>A custom validation function to be called during field validation ({@link #validateValue})
56376      * (defaults to <tt>null</tt>). If specified, this function will be called first, allowing the
56377      * developer to override the default validation process.</p>
56378      * <br><p>This function will be passed the following Parameters:</p>
56379      * <div class="mdetail-params"><ul>
56380      * <li><code>value</code>: <i>Mixed</i>
56381      * <div class="sub-desc">The current field value</div></li>
56382      * </ul></div>
56383      * <br><p>This function is to Return:</p>
56384      * <div class="mdetail-params"><ul>
56385      * <li><code>true</code>: <i>Boolean</i>
56386      * <div class="sub-desc"><code>true</code> if the value is valid</div></li>
56387      * <li><code>msg</code>: <i>String</i>
56388      * <div class="sub-desc">An error message if the value is invalid</div></li>
56389      * </ul></div>
56390      */
56391     validator : null,
56392     /**
56393      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation
56394      * (defaults to <tt>null</tt>). If the test fails, the field will be marked invalid using
56395      * <b><tt>{@link #regexText}</tt></b>.
56396      */
56397     regex : null,
56398     /**
56399      * @cfg {String} regexText The error text to display if <b><tt>{@link #regex}</tt></b> is used and the
56400      * test fails during validation (defaults to <tt>''</tt>)
56401      */
56402     regexText : '',
56403     /**
56404      * @cfg {String} emptyText The default text to place into an empty field (defaults to <tt>null</tt>).
56405      * <b>Note</b>: that this value will be submitted to the server if this field is enabled and configured
56406      * with a {@link #name}.
56407      */
56408     emptyText : null,
56409     /**
56410      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the <b><tt>{@link #emptyText}</tt></b>
56411      * (defaults to <tt>'x-form-empty-field'</tt>).  This class is automatically added and removed as needed
56412      * depending on the current field value.
56413      */
56414     emptyClass : 'x-form-empty-field',
56415
56416     /**
56417      * @cfg {Boolean} enableKeyEvents <tt>true</tt> to enable the proxying of key events for the HTML input
56418      * field (defaults to <tt>false</tt>)
56419      */
56420
56421     initComponent : function(){
56422         Ext.form.TextField.superclass.initComponent.call(this);
56423         this.addEvents(
56424             /**
56425              * @event autosize
56426              * Fires when the <tt><b>{@link #autoSize}</b></tt> function is triggered. The field may or
56427              * may not have actually changed size according to the default logic, but this event provides
56428              * a hook for the developer to apply additional logic at runtime to resize the field if needed.
56429              * @param {Ext.form.Field} this This text field
56430              * @param {Number} width The new field width
56431              */
56432             'autosize',
56433
56434             /**
56435              * @event keydown
56436              * Keydown input field event. This event only fires if <tt><b>{@link #enableKeyEvents}</b></tt>
56437              * is set to true.
56438              * @param {Ext.form.TextField} this This text field
56439              * @param {Ext.EventObject} e
56440              */
56441             'keydown',
56442             /**
56443              * @event keyup
56444              * Keyup input field event. This event only fires if <tt><b>{@link #enableKeyEvents}</b></tt>
56445              * is set to true.
56446              * @param {Ext.form.TextField} this This text field
56447              * @param {Ext.EventObject} e
56448              */
56449             'keyup',
56450             /**
56451              * @event keypress
56452              * Keypress input field event. This event only fires if <tt><b>{@link #enableKeyEvents}</b></tt>
56453              * is set to true.
56454              * @param {Ext.form.TextField} this This text field
56455              * @param {Ext.EventObject} e
56456              */
56457             'keypress'
56458         );
56459     },
56460
56461     // private
56462     initEvents : function(){
56463         Ext.form.TextField.superclass.initEvents.call(this);
56464         if(this.validationEvent == 'keyup'){
56465             this.validationTask = new Ext.util.DelayedTask(this.validate, this);
56466             this.mon(this.el, 'keyup', this.filterValidation, this);
56467         }
56468         else if(this.validationEvent !== false && this.validationEvent != 'blur'){
56469                 this.mon(this.el, this.validationEvent, this.validate, this, {buffer: this.validationDelay});
56470         }
56471         if(this.selectOnFocus || this.emptyText){            
56472             this.mon(this.el, 'mousedown', this.onMouseDown, this);
56473             
56474             if(this.emptyText){
56475                 this.applyEmptyText();
56476             }
56477         }
56478         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Ext.form.VTypes[this.vtype+'Mask']))){
56479                 this.mon(this.el, 'keypress', this.filterKeys, this);
56480         }
56481         if(this.grow){
56482                 this.mon(this.el, 'keyup', this.onKeyUpBuffered, this, {buffer: 50});
56483                         this.mon(this.el, 'click', this.autoSize, this);
56484         }
56485         if(this.enableKeyEvents){
56486             this.mon(this.el, {
56487                 scope: this,
56488                 keyup: this.onKeyUp,
56489                 keydown: this.onKeyDown,
56490                 keypress: this.onKeyPress
56491             });
56492         }
56493     },
56494     
56495     onMouseDown: function(e){
56496         if(!this.hasFocus){
56497             this.mon(this.el, 'mouseup', Ext.emptyFn, this, { single: true, preventDefault: true });
56498         }
56499     },
56500
56501     processValue : function(value){
56502         if(this.stripCharsRe){
56503             var newValue = value.replace(this.stripCharsRe, '');
56504             if(newValue !== value){
56505                 this.setRawValue(newValue);
56506                 return newValue;
56507             }
56508         }
56509         return value;
56510     },
56511
56512     filterValidation : function(e){
56513         if(!e.isNavKeyPress()){
56514             this.validationTask.delay(this.validationDelay);
56515         }
56516     },
56517     
56518     //private
56519     onDisable: function(){
56520         Ext.form.TextField.superclass.onDisable.call(this);
56521         if(Ext.isIE){
56522             this.el.dom.unselectable = 'on';
56523         }
56524     },
56525     
56526     //private
56527     onEnable: function(){
56528         Ext.form.TextField.superclass.onEnable.call(this);
56529         if(Ext.isIE){
56530             this.el.dom.unselectable = '';
56531         }
56532     },
56533
56534     // private
56535     onKeyUpBuffered : function(e){
56536         if(this.doAutoSize(e)){
56537             this.autoSize();
56538         }
56539     },
56540     
56541     // private
56542     doAutoSize : function(e){
56543         return !e.isNavKeyPress();
56544     },
56545
56546     // private
56547     onKeyUp : function(e){
56548         this.fireEvent('keyup', this, e);
56549     },
56550
56551     // private
56552     onKeyDown : function(e){
56553         this.fireEvent('keydown', this, e);
56554     },
56555
56556     // private
56557     onKeyPress : function(e){
56558         this.fireEvent('keypress', this, e);
56559     },
56560
56561     /**
56562      * Resets the current field value to the originally-loaded value and clears any validation messages.
56563      * Also adds <tt><b>{@link #emptyText}</b></tt> and <tt><b>{@link #emptyClass}</b></tt> if the
56564      * original value was blank.
56565      */
56566     reset : function(){
56567         Ext.form.TextField.superclass.reset.call(this);
56568         this.applyEmptyText();
56569     },
56570
56571     applyEmptyText : function(){
56572         if(this.rendered && this.emptyText && this.getRawValue().length < 1 && !this.hasFocus){
56573             this.setRawValue(this.emptyText);
56574             this.el.addClass(this.emptyClass);
56575         }
56576     },
56577
56578     // private
56579     preFocus : function(){
56580         var el = this.el;
56581         if(this.emptyText){
56582             if(el.dom.value == this.emptyText){
56583                 this.setRawValue('');
56584             }
56585             el.removeClass(this.emptyClass);
56586         }
56587         if(this.selectOnFocus){
56588             el.dom.select();
56589         }
56590     },
56591
56592     // private
56593     postBlur : function(){
56594         this.applyEmptyText();
56595     },
56596
56597     // private
56598     filterKeys : function(e){
56599         if(e.ctrlKey){
56600             return;
56601         }
56602         var k = e.getKey();
56603         if(Ext.isGecko && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
56604             return;
56605         }
56606         var cc = String.fromCharCode(e.getCharCode());
56607         if(!Ext.isGecko && e.isSpecialKey() && !cc){
56608             return;
56609         }
56610         if(!this.maskRe.test(cc)){
56611             e.stopEvent();
56612         }
56613     },
56614
56615     setValue : function(v){
56616         if(this.emptyText && this.el && !Ext.isEmpty(v)){
56617             this.el.removeClass(this.emptyClass);
56618         }
56619         Ext.form.TextField.superclass.setValue.apply(this, arguments);
56620         this.applyEmptyText();
56621         this.autoSize();
56622         return this;
56623     },
56624
56625     /**
56626      * <p>Validates a value according to the field's validation rules and marks the field as invalid
56627      * if the validation fails. Validation rules are processed in the following order:</p>
56628      * <div class="mdetail-params"><ul>
56629      * 
56630      * <li><b>1. Field specific validator</b>
56631      * <div class="sub-desc">
56632      * <p>A validator offers a way to customize and reuse a validation specification.
56633      * If a field is configured with a <code>{@link #validator}</code>
56634      * function, it will be passed the current field value.  The <code>{@link #validator}</code>
56635      * function is expected to return either:
56636      * <div class="mdetail-params"><ul>
56637      * <li>Boolean <tt>true</tt> if the value is valid (validation continues).</li>
56638      * <li>a String to represent the invalid message if invalid (validation halts).</li>
56639      * </ul></div>
56640      * </div></li>
56641      * 
56642      * <li><b>2. Basic Validation</b>
56643      * <div class="sub-desc">
56644      * <p>If the <code>{@link #validator}</code> has not halted validation,
56645      * basic validation proceeds as follows:</p>
56646      * 
56647      * <div class="mdetail-params"><ul>
56648      * 
56649      * <li><code>{@link #allowBlank}</code> : (Invalid message =
56650      * <code>{@link #emptyText}</code>)<div class="sub-desc">
56651      * Depending on the configuration of <code>{@link #allowBlank}</code>, a
56652      * blank field will cause validation to halt at this step and return
56653      * Boolean true or false accordingly.  
56654      * </div></li>
56655      * 
56656      * <li><code>{@link #minLength}</code> : (Invalid message =
56657      * <code>{@link #minLengthText}</code>)<div class="sub-desc">
56658      * If the passed value does not satisfy the <code>{@link #minLength}</code>
56659      * specified, validation halts.
56660      * </div></li>
56661      * 
56662      * <li><code>{@link #maxLength}</code> : (Invalid message =
56663      * <code>{@link #maxLengthText}</code>)<div class="sub-desc">
56664      * If the passed value does not satisfy the <code>{@link #maxLength}</code>
56665      * specified, validation halts.
56666      * </div></li>
56667      * 
56668      * </ul></div>
56669      * </div></li>
56670      * 
56671      * <li><b>3. Preconfigured Validation Types (VTypes)</b>
56672      * <div class="sub-desc">
56673      * <p>If none of the prior validation steps halts validation, a field
56674      * configured with a <code>{@link #vtype}</code> will utilize the
56675      * corresponding {@link Ext.form.VTypes VTypes} validation function.
56676      * If invalid, either the field's <code>{@link #vtypeText}</code> or
56677      * the VTypes vtype Text property will be used for the invalid message.
56678      * Keystrokes on the field will be filtered according to the VTypes
56679      * vtype Mask property.</p>
56680      * </div></li>
56681      * 
56682      * <li><b>4. Field specific regex test</b>
56683      * <div class="sub-desc">
56684      * <p>If none of the prior validation steps halts validation, a field's
56685      * configured <code>{@link #regex}</code> test will be processed.
56686      * The invalid message for this test is configured with
56687      * <code>{@link #regexText}</code>.</p>
56688      * </div></li>
56689      * 
56690      * @param {Mixed} value The value to validate
56691      * @return {Boolean} True if the value is valid, else false
56692      */
56693     validateValue : function(value){
56694         if(Ext.isFunction(this.validator)){
56695             var msg = this.validator(value);
56696             if(msg !== true){
56697                 this.markInvalid(msg);
56698                 return false;
56699             }
56700         }
56701         if(value.length < 1 || value === this.emptyText){ // if it's blank
56702              if(this.allowBlank){
56703                  this.clearInvalid();
56704                  return true;
56705              }else{
56706                  this.markInvalid(this.blankText);
56707                  return false;
56708              }
56709         }
56710         if(value.length < this.minLength){
56711             this.markInvalid(String.format(this.minLengthText, this.minLength));
56712             return false;
56713         }
56714         if(value.length > this.maxLength){
56715             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
56716             return false;
56717         }       
56718         if(this.vtype){
56719             var vt = Ext.form.VTypes;
56720             if(!vt[this.vtype](value, this)){
56721                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
56722                 return false;
56723             }
56724         }
56725         if(this.regex && !this.regex.test(value)){
56726             this.markInvalid(this.regexText);
56727             return false;
56728         }
56729         return true;
56730     },
56731
56732     /**
56733      * Selects text in this field
56734      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
56735      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
56736      */
56737     selectText : function(start, end){
56738         var v = this.getRawValue();
56739         var doFocus = false;
56740         if(v.length > 0){
56741             start = start === undefined ? 0 : start;
56742             end = end === undefined ? v.length : end;
56743             var d = this.el.dom;
56744             if(d.setSelectionRange){
56745                 d.setSelectionRange(start, end);
56746             }else if(d.createTextRange){
56747                 var range = d.createTextRange();
56748                 range.moveStart('character', start);
56749                 range.moveEnd('character', end-v.length);
56750                 range.select();
56751             }
56752             doFocus = Ext.isGecko || Ext.isOpera;
56753         }else{
56754             doFocus = true;
56755         }
56756         if(doFocus){
56757             this.focus();
56758         }
56759     },
56760
56761     /**
56762      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
56763      * This only takes effect if <tt><b>{@link #grow}</b> = true</tt>, and fires the {@link #autosize} event.
56764      */
56765     autoSize : function(){
56766         if(!this.grow || !this.rendered){
56767             return;
56768         }
56769         if(!this.metrics){
56770             this.metrics = Ext.util.TextMetrics.createInstance(this.el);
56771         }
56772         var el = this.el;
56773         var v = el.dom.value;
56774         var d = document.createElement('div');
56775         d.appendChild(document.createTextNode(v));
56776         v = d.innerHTML;
56777         Ext.removeNode(d);
56778         d = null;
56779         v += '&#160;';
56780         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
56781         this.el.setWidth(w);
56782         this.fireEvent('autosize', this, w);
56783     },
56784         
56785         onDestroy: function(){
56786                 if(this.validationTask){
56787                         this.validationTask.cancel();
56788                         this.validationTask = null;
56789                 }
56790                 Ext.form.TextField.superclass.onDestroy.call(this);
56791         }
56792 });
56793 Ext.reg('textfield', Ext.form.TextField);
56794 /**
56795  * @class Ext.form.TriggerField
56796  * @extends Ext.form.TextField
56797  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
56798  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
56799  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
56800  * for which you can provide a custom implementation.  For example:
56801  * <pre><code>
56802 var trigger = new Ext.form.TriggerField();
56803 trigger.onTriggerClick = myTriggerFn;
56804 trigger.applyToMarkup('my-field');
56805 </code></pre>
56806  *
56807  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
56808  * {@link Ext.form.DateField} and {@link Ext.form.ComboBox} are perfect examples of this.
56809  *
56810  * @constructor
56811  * Create a new TriggerField.
56812  * @param {Object} config Configuration options (valid {@Ext.form.TextField} config options will also be applied
56813  * to the base TextField)
56814  * @xtype trigger
56815  */
56816 Ext.form.TriggerField = Ext.extend(Ext.form.TextField,  {
56817     /**
56818      * @cfg {String} triggerClass
56819      * An additional CSS class used to style the trigger button.  The trigger will always get the
56820      * class <tt>'x-form-trigger'</tt> by default and <tt>triggerClass</tt> will be <b>appended</b> if specified.
56821      */
56822     /**
56823      * @cfg {Mixed} triggerConfig
56824      * <p>A {@link Ext.DomHelper DomHelper} config object specifying the structure of the
56825      * trigger element for this Field. (Optional).</p>
56826      * <p>Specify this when you need a customized element to act as the trigger button for a TriggerField.</p>
56827      * <p>Note that when using this option, it is the developer's responsibility to ensure correct sizing, positioning
56828      * and appearance of the trigger.  Defaults to:</p>
56829      * <pre><code>{tag: "img", src: Ext.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass}</code></pre>
56830      */
56831     /**
56832      * @cfg {String/Object} autoCreate <p>A {@link Ext.DomHelper DomHelper} element spec, or true for a default
56833      * element spec. Used to create the {@link Ext.Component#getEl Element} which will encapsulate this Component.
56834      * See <tt>{@link Ext.Component#autoEl autoEl}</tt> for details.  Defaults to:</p>
56835      * <pre><code>{tag: "input", type: "text", size: "16", autocomplete: "off"}</code></pre>
56836      */
56837     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
56838     /**
56839      * @cfg {Boolean} hideTrigger <tt>true</tt> to hide the trigger element and display only the base
56840      * text field (defaults to <tt>false</tt>)
56841      */
56842     hideTrigger:false,
56843     /**
56844      * @cfg {Boolean} editable <tt>false</tt> to prevent the user from typing text directly into the field,
56845      * the field will only respond to a click on the trigger to set the value. (defaults to <tt>true</tt>).
56846      */
56847     editable: true,
56848     /**
56849      * @cfg {Boolean} readOnly <tt>true</tt> to prevent the user from changing the field, and
56850      * hides the trigger.  Superceeds the editable and hideTrigger options if the value is true.
56851      * (defaults to <tt>false</tt>)
56852      */
56853     readOnly: false,
56854     /**
56855      * @cfg {String} wrapFocusClass The class added to the to the wrap of the trigger element. Defaults to
56856      * <tt>x-trigger-wrap-focus</tt>.
56857      */
56858     wrapFocusClass: 'x-trigger-wrap-focus',
56859     /**
56860      * @hide
56861      * @method autoSize
56862      */
56863     autoSize: Ext.emptyFn,
56864     // private
56865     monitorTab : true,
56866     // private
56867     deferHeight : true,
56868     // private
56869     mimicing : false,
56870
56871     actionMode: 'wrap',
56872
56873     defaultTriggerWidth: 17,
56874
56875     // private
56876     onResize : function(w, h){
56877         Ext.form.TriggerField.superclass.onResize.call(this, w, h);
56878         var tw = this.getTriggerWidth();
56879         if(Ext.isNumber(w)){
56880             this.el.setWidth(w - tw);
56881         }
56882         this.wrap.setWidth(this.el.getWidth() + tw);
56883     },
56884
56885     getTriggerWidth: function(){
56886         var tw = this.trigger.getWidth();
56887         if(!this.hideTrigger && tw === 0){
56888             tw = this.defaultTriggerWidth;
56889         }
56890         return tw;
56891     },
56892
56893     // private
56894     alignErrorIcon : function(){
56895         if(this.wrap){
56896             this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
56897         }
56898     },
56899
56900     // private
56901     onRender : function(ct, position){
56902         this.doc = Ext.isIE ? Ext.getBody() : Ext.getDoc();
56903         Ext.form.TriggerField.superclass.onRender.call(this, ct, position);
56904
56905         this.wrap = this.el.wrap({cls: 'x-form-field-wrap x-form-field-trigger-wrap'});
56906         this.trigger = this.wrap.createChild(this.triggerConfig ||
56907                 {tag: "img", src: Ext.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
56908         this.initTrigger();
56909         if(!this.width){
56910             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
56911         }
56912         this.resizeEl = this.positionEl = this.wrap;
56913     },
56914
56915     updateEditState: function(){
56916         if(this.rendered){
56917             if (this.readOnly) {
56918                 this.el.dom.readOnly = true;
56919                 this.el.addClass('x-trigger-noedit');
56920                 this.mun(this.el, 'click', this.onTriggerClick, this);
56921                 this.trigger.setDisplayed(false);
56922             } else {
56923                 if (!this.editable) {
56924                     this.el.dom.readOnly = true;
56925                     this.el.addClass('x-trigger-noedit');
56926                     this.mon(this.el, 'click', this.onTriggerClick, this);
56927                 } else {
56928                     this.el.dom.readOnly = false;
56929                     this.el.removeClass('x-trigger-noedit');
56930                     this.mun(this.el, 'click', this.onTriggerClick, this);
56931                 }
56932                 this.trigger.setDisplayed(!this.hideTrigger);
56933             }
56934             this.onResize(this.width || this.wrap.getWidth());
56935         }
56936     },
56937
56938     setHideTrigger: function(hideTrigger){
56939         if(hideTrigger != this.hideTrigger){
56940             this.hideTrigger = hideTrigger;
56941             this.updateEditState();
56942         }
56943     },
56944
56945     /**
56946      * @param {Boolean} value True to allow the user to directly edit the field text
56947      * Allow or prevent the user from directly editing the field text.  If false is passed,
56948      * the user will only be able to modify the field using the trigger.  Will also add
56949      * a click event to the text field which will call the trigger. This method
56950      * is the runtime equivalent of setting the 'editable' config option at config time.
56951      */
56952     setEditable: function(editable){
56953         if(editable != this.editable){
56954             this.editable = editable;
56955             this.updateEditState();
56956         }
56957     },
56958
56959     /**
56960      * @param {Boolean} value True to prevent the user changing the field and explicitly
56961      * hide the trigger.
56962      * Setting this to true will superceed settings editable and hideTrigger.
56963      * Setting this to false will defer back to editable and hideTrigger. This method
56964      * is the runtime equivalent of setting the 'readOnly' config option at config time.
56965      */
56966     setReadOnly: function(readOnly){
56967         if(readOnly != this.readOnly){
56968             this.readOnly = readOnly;
56969             this.updateEditState();
56970         }
56971     },
56972
56973     afterRender : function(){
56974         Ext.form.TriggerField.superclass.afterRender.call(this);
56975         this.updateEditState();
56976     },
56977
56978     // private
56979     initTrigger : function(){
56980         this.mon(this.trigger, 'click', this.onTriggerClick, this, {preventDefault:true});
56981         this.trigger.addClassOnOver('x-form-trigger-over');
56982         this.trigger.addClassOnClick('x-form-trigger-click');
56983     },
56984
56985     // private
56986     onDestroy : function(){
56987         Ext.destroy(this.trigger, this.wrap);
56988         if (this.mimicing){
56989             this.doc.un('mousedown', this.mimicBlur, this);
56990         }
56991         delete this.doc;
56992         Ext.form.TriggerField.superclass.onDestroy.call(this);
56993     },
56994
56995     // private
56996     onFocus : function(){
56997         Ext.form.TriggerField.superclass.onFocus.call(this);
56998         if(!this.mimicing){
56999             this.wrap.addClass(this.wrapFocusClass);
57000             this.mimicing = true;
57001             this.doc.on('mousedown', this.mimicBlur, this, {delay: 10});
57002             if(this.monitorTab){
57003                 this.on('specialkey', this.checkTab, this);
57004             }
57005         }
57006     },
57007
57008     // private
57009     checkTab : function(me, e){
57010         if(e.getKey() == e.TAB){
57011             this.triggerBlur();
57012         }
57013     },
57014
57015     // private
57016     onBlur : Ext.emptyFn,
57017
57018     // private
57019     mimicBlur : function(e){
57020         if(!this.isDestroyed && !this.wrap.contains(e.target) && this.validateBlur(e)){
57021             this.triggerBlur();
57022         }
57023     },
57024
57025     // private
57026     triggerBlur : function(){
57027         this.mimicing = false;
57028         this.doc.un('mousedown', this.mimicBlur, this);
57029         if(this.monitorTab && this.el){
57030             this.un('specialkey', this.checkTab, this);
57031         }
57032         Ext.form.TriggerField.superclass.onBlur.call(this);
57033         if(this.wrap){
57034             this.wrap.removeClass(this.wrapFocusClass);
57035         }
57036     },
57037
57038     beforeBlur : Ext.emptyFn,
57039
57040     // private
57041     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
57042     validateBlur : function(e){
57043         return true;
57044     },
57045
57046     /**
57047      * The function that should handle the trigger's click event.  This method does nothing by default
57048      * until overridden by an implementing function.  See Ext.form.ComboBox and Ext.form.DateField for
57049      * sample implementations.
57050      * @method
57051      * @param {EventObject} e
57052      */
57053     onTriggerClick : Ext.emptyFn
57054
57055     /**
57056      * @cfg {Boolean} grow @hide
57057      */
57058     /**
57059      * @cfg {Number} growMin @hide
57060      */
57061     /**
57062      * @cfg {Number} growMax @hide
57063      */
57064 });
57065
57066 /**
57067  * @class Ext.form.TwinTriggerField
57068  * @extends Ext.form.TriggerField
57069  * TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
57070  * to be extended by an implementing class.  For an example of implementing this class, see the custom
57071  * SearchField implementation here:
57072  * <a href="http://extjs.com/deploy/ext/examples/form/custom.html">http://extjs.com/deploy/ext/examples/form/custom.html</a>
57073  */
57074 Ext.form.TwinTriggerField = Ext.extend(Ext.form.TriggerField, {
57075     /**
57076      * @cfg {Mixed} triggerConfig
57077      * <p>A {@link Ext.DomHelper DomHelper} config object specifying the structure of the trigger elements
57078      * for this Field. (Optional).</p>
57079      * <p>Specify this when you need a customized element to contain the two trigger elements for this Field.
57080      * Each trigger element must be marked by the CSS class <tt>x-form-trigger</tt> (also see
57081      * <tt>{@link #trigger1Class}</tt> and <tt>{@link #trigger2Class}</tt>).</p>
57082      * <p>Note that when using this option, it is the developer's responsibility to ensure correct sizing,
57083      * positioning and appearance of the triggers.</p>
57084      */
57085     /**
57086      * @cfg {String} trigger1Class
57087      * An additional CSS class used to style the trigger button.  The trigger will always get the
57088      * class <tt>'x-form-trigger'</tt> by default and <tt>triggerClass</tt> will be <b>appended</b> if specified.
57089      */
57090     /**
57091      * @cfg {String} trigger2Class
57092      * An additional CSS class used to style the trigger button.  The trigger will always get the
57093      * class <tt>'x-form-trigger'</tt> by default and <tt>triggerClass</tt> will be <b>appended</b> if specified.
57094      */
57095
57096     initComponent : function(){
57097         Ext.form.TwinTriggerField.superclass.initComponent.call(this);
57098
57099         this.triggerConfig = {
57100             tag:'span', cls:'x-form-twin-triggers', cn:[
57101             {tag: "img", src: Ext.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
57102             {tag: "img", src: Ext.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
57103         ]};
57104     },
57105
57106     getTrigger : function(index){
57107         return this.triggers[index];
57108     },
57109
57110     initTrigger : function(){
57111         var ts = this.trigger.select('.x-form-trigger', true);
57112         var triggerField = this;
57113         ts.each(function(t, all, index){
57114             var triggerIndex = 'Trigger'+(index+1);
57115             t.hide = function(){
57116                 var w = triggerField.wrap.getWidth();
57117                 this.dom.style.display = 'none';
57118                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
57119                 this['hidden' + triggerIndex] = true;
57120             };
57121             t.show = function(){
57122                 var w = triggerField.wrap.getWidth();
57123                 this.dom.style.display = '';
57124                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
57125                 this['hidden' + triggerIndex] = false;
57126             };
57127
57128             if(this['hide'+triggerIndex]){
57129                 t.dom.style.display = 'none';
57130                 this['hidden' + triggerIndex] = true;
57131             }
57132             this.mon(t, 'click', this['on'+triggerIndex+'Click'], this, {preventDefault:true});
57133             t.addClassOnOver('x-form-trigger-over');
57134             t.addClassOnClick('x-form-trigger-click');
57135         }, this);
57136         this.triggers = ts.elements;
57137     },
57138
57139     getTriggerWidth: function(){
57140         var tw = 0;
57141         Ext.each(this.triggers, function(t, index){
57142             var triggerIndex = 'Trigger' + (index + 1),
57143                 w = t.getWidth();
57144             if(w === 0 && !this['hidden' + triggerIndex]){
57145                 tw += this.defaultTriggerWidth;
57146             }else{
57147                 tw += w;
57148             }
57149         }, this);
57150         return tw;
57151     },
57152
57153     // private
57154     onDestroy : function() {
57155         Ext.destroy(this.triggers);
57156         Ext.form.TwinTriggerField.superclass.onDestroy.call(this);
57157     },
57158
57159     /**
57160      * The function that should handle the trigger's click event.  This method does nothing by default
57161      * until overridden by an implementing function. See {@link Ext.form.TriggerField#onTriggerClick}
57162      * for additional information.
57163      * @method
57164      * @param {EventObject} e
57165      */
57166     onTrigger1Click : Ext.emptyFn,
57167     /**
57168      * The function that should handle the trigger's click event.  This method does nothing by default
57169      * until overridden by an implementing function. See {@link Ext.form.TriggerField#onTriggerClick}
57170      * for additional information.
57171      * @method
57172      * @param {EventObject} e
57173      */
57174     onTrigger2Click : Ext.emptyFn
57175 });
57176 Ext.reg('trigger', Ext.form.TriggerField);
57177 /**
57178  * @class Ext.form.TextArea
57179  * @extends Ext.form.TextField
57180  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
57181  * support for auto-sizing.
57182  * @constructor
57183  * Creates a new TextArea
57184  * @param {Object} config Configuration options
57185  * @xtype textarea
57186  */
57187 Ext.form.TextArea = Ext.extend(Ext.form.TextField,  {
57188     /**
57189      * @cfg {Number} growMin The minimum height to allow when <tt>{@link Ext.form.TextField#grow grow}=true</tt>
57190      * (defaults to <tt>60</tt>)
57191      */
57192     growMin : 60,
57193     /**
57194      * @cfg {Number} growMax The maximum height to allow when <tt>{@link Ext.form.TextField#grow grow}=true</tt>
57195      * (defaults to <tt>1000</tt>)
57196      */
57197     growMax: 1000,
57198     growAppend : '&#160;\n&#160;',
57199
57200     enterIsSpecial : false,
57201
57202     /**
57203      * @cfg {Boolean} preventScrollbars <tt>true</tt> to prevent scrollbars from appearing regardless of how much text is
57204      * in the field. This option is only relevant when {@link #grow} is <tt>true</tt>. Equivalent to setting overflow: hidden, defaults to 
57205      * <tt>false</tt>.
57206      */
57207     preventScrollbars: false,
57208     /**
57209      * @cfg {String/Object} autoCreate <p>A {@link Ext.DomHelper DomHelper} element spec, or true for a default
57210      * element spec. Used to create the {@link Ext.Component#getEl Element} which will encapsulate this Component.
57211      * See <tt>{@link Ext.Component#autoEl autoEl}</tt> for details.  Defaults to:</p>
57212      * <pre><code>{tag: "textarea", style: "width:100px;height:60px;", autocomplete: "off"}</code></pre>
57213      */
57214
57215     // private
57216     onRender : function(ct, position){
57217         if(!this.el){
57218             this.defaultAutoCreate = {
57219                 tag: "textarea",
57220                 style:"width:100px;height:60px;",
57221                 autocomplete: "off"
57222             };
57223         }
57224         Ext.form.TextArea.superclass.onRender.call(this, ct, position);
57225         if(this.grow){
57226             this.textSizeEl = Ext.DomHelper.append(document.body, {
57227                 tag: "pre", cls: "x-form-grow-sizer"
57228             });
57229             if(this.preventScrollbars){
57230                 this.el.setStyle("overflow", "hidden");
57231             }
57232             this.el.setHeight(this.growMin);
57233         }
57234     },
57235
57236     onDestroy : function(){
57237         Ext.removeNode(this.textSizeEl);
57238         Ext.form.TextArea.superclass.onDestroy.call(this);
57239     },
57240
57241     fireKey : function(e){
57242         if(e.isSpecialKey() && (this.enterIsSpecial || (e.getKey() != e.ENTER || e.hasModifier()))){
57243             this.fireEvent("specialkey", this, e);
57244         }
57245     },
57246     
57247     // private
57248     doAutoSize : function(e){
57249         return !e.isNavKeyPress() || e.getKey() == e.ENTER;
57250     },
57251
57252     /**
57253      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
57254      * This only takes effect if grow = true, and fires the {@link #autosize} event if the height changes.
57255      */
57256     autoSize: function(){
57257         if(!this.grow || !this.textSizeEl){
57258             return;
57259         }
57260         var el = this.el,
57261             v = Ext.util.Format.htmlEncode(el.dom.value),
57262             ts = this.textSizeEl,
57263             h;
57264             
57265         Ext.fly(ts).setWidth(this.el.getWidth());
57266         if(v.length < 1){
57267             v = "&#160;&#160;";
57268         }else{
57269             v += this.growAppend;
57270             if(Ext.isIE){
57271                 v = v.replace(/\n/g, '&#160;<br />');
57272             }
57273         }
57274         ts.innerHTML = v;
57275         h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
57276         if(h != this.lastHeight){
57277             this.lastHeight = h;
57278             this.el.setHeight(h);
57279             this.fireEvent("autosize", this, h);
57280         }
57281     }
57282 });
57283 Ext.reg('textarea', Ext.form.TextArea);/**
57284  * @class Ext.form.NumberField
57285  * @extends Ext.form.TextField
57286  * Numeric text field that provides automatic keystroke filtering and numeric validation.
57287  * @constructor
57288  * Creates a new NumberField
57289  * @param {Object} config Configuration options
57290  * @xtype numberfield
57291  */
57292 Ext.form.NumberField = Ext.extend(Ext.form.TextField,  {
57293     /**
57294      * @cfg {RegExp} stripCharsRe @hide
57295      */
57296     /**
57297      * @cfg {RegExp} maskRe @hide
57298      */
57299     /**
57300      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
57301      */
57302     fieldClass: "x-form-field x-form-num-field",
57303     /**
57304      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
57305      */
57306     allowDecimals : true,
57307     /**
57308      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
57309      */
57310     decimalSeparator : ".",
57311     /**
57312      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
57313      */
57314     decimalPrecision : 2,
57315     /**
57316      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
57317      */
57318     allowNegative : true,
57319     /**
57320      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
57321      */
57322     minValue : Number.NEGATIVE_INFINITY,
57323     /**
57324      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
57325      */
57326     maxValue : Number.MAX_VALUE,
57327     /**
57328      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
57329      */
57330     minText : "The minimum value for this field is {0}",
57331     /**
57332      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
57333      */
57334     maxText : "The maximum value for this field is {0}",
57335     /**
57336      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
57337      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
57338      */
57339     nanText : "{0} is not a valid number",
57340     /**
57341      * @cfg {String} baseChars The base set of characters to evaluate as valid numbers (defaults to '0123456789').
57342      */
57343     baseChars : "0123456789",
57344
57345     // private
57346     initEvents : function(){
57347         var allowed = this.baseChars + '';
57348         if (this.allowDecimals) {
57349             allowed += this.decimalSeparator;
57350         }
57351         if (this.allowNegative) {
57352             allowed += '-';
57353         }
57354         this.maskRe = new RegExp('[' + Ext.escapeRe(allowed) + ']');
57355         Ext.form.NumberField.superclass.initEvents.call(this);
57356     },
57357
57358     // private
57359     validateValue : function(value){
57360         if(!Ext.form.NumberField.superclass.validateValue.call(this, value)){
57361             return false;
57362         }
57363         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
57364              return true;
57365         }
57366         value = String(value).replace(this.decimalSeparator, ".");
57367         if(isNaN(value)){
57368             this.markInvalid(String.format(this.nanText, value));
57369             return false;
57370         }
57371         var num = this.parseValue(value);
57372         if(num < this.minValue){
57373             this.markInvalid(String.format(this.minText, this.minValue));
57374             return false;
57375         }
57376         if(num > this.maxValue){
57377             this.markInvalid(String.format(this.maxText, this.maxValue));
57378             return false;
57379         }
57380         return true;
57381     },
57382
57383     getValue : function(){
57384         return this.fixPrecision(this.parseValue(Ext.form.NumberField.superclass.getValue.call(this)));
57385     },
57386
57387     setValue : function(v){
57388         v = Ext.isNumber(v) ? v : parseFloat(String(v).replace(this.decimalSeparator, "."));
57389         v = isNaN(v) ? '' : String(v).replace(".", this.decimalSeparator);
57390         return Ext.form.NumberField.superclass.setValue.call(this, v);
57391     },
57392     
57393     /**
57394      * Replaces any existing {@link #minValue} with the new value.
57395      * @param {Number} value The minimum value
57396      */
57397     setMinValue : function(value){
57398         this.minValue = Ext.num(value, Number.NEGATIVE_INFINITY);
57399     },
57400     
57401     /**
57402      * Replaces any existing {@link #maxValue} with the new value.
57403      * @param {Number} value The maximum value
57404      */
57405     setMaxValue : function(value){
57406         this.maxValue = Ext.num(value, Number.MAX_VALUE);    
57407     },
57408
57409     // private
57410     parseValue : function(value){
57411         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
57412         return isNaN(value) ? '' : value;
57413     },
57414
57415     // private
57416     fixPrecision : function(value){
57417         var nan = isNaN(value);
57418         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
57419            return nan ? '' : value;
57420         }
57421         return parseFloat(parseFloat(value).toFixed(this.decimalPrecision));
57422     },
57423
57424     beforeBlur : function(){
57425         var v = this.parseValue(this.getRawValue());
57426         if(!Ext.isEmpty(v)){
57427             this.setValue(this.fixPrecision(v));
57428         }
57429     }
57430 });
57431 Ext.reg('numberfield', Ext.form.NumberField);/**
57432  * @class Ext.form.DateField
57433  * @extends Ext.form.TriggerField
57434  * Provides a date input field with a {@link Ext.DatePicker} dropdown and automatic date validation.
57435  * @constructor
57436  * Create a new DateField
57437  * @param {Object} config
57438  * @xtype datefield
57439  */
57440 Ext.form.DateField = Ext.extend(Ext.form.TriggerField,  {
57441     /**
57442      * @cfg {String} format
57443      * The default date format string which can be overriden for localization support.  The format must be
57444      * valid according to {@link Date#parseDate} (defaults to <tt>'m/d/Y'</tt>).
57445      */
57446     format : "m/d/Y",
57447     /**
57448      * @cfg {String} altFormats
57449      * Multiple date formats separated by "<tt>|</tt>" to try when parsing a user input value and it
57450      * does not match the defined format (defaults to
57451      * <tt>'m/d/Y|n/j/Y|n/j/y|m/j/y|n/d/y|m/j/Y|n/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d|Y-m-d'</tt>).
57452      */
57453     altFormats : "m/d/Y|n/j/Y|n/j/y|m/j/y|n/d/y|m/j/Y|n/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d|Y-m-d",
57454     /**
57455      * @cfg {String} disabledDaysText
57456      * The tooltip to display when the date falls on a disabled day (defaults to <tt>'Disabled'</tt>)
57457      */
57458     disabledDaysText : "Disabled",
57459     /**
57460      * @cfg {String} disabledDatesText
57461      * The tooltip text to display when the date falls on a disabled date (defaults to <tt>'Disabled'</tt>)
57462      */
57463     disabledDatesText : "Disabled",
57464     /**
57465      * @cfg {String} minText
57466      * The error text to display when the date in the cell is before <tt>{@link #minValue}</tt> (defaults to
57467      * <tt>'The date in this field must be after {minValue}'</tt>).
57468      */
57469     minText : "The date in this field must be equal to or after {0}",
57470     /**
57471      * @cfg {String} maxText
57472      * The error text to display when the date in the cell is after <tt>{@link #maxValue}</tt> (defaults to
57473      * <tt>'The date in this field must be before {maxValue}'</tt>).
57474      */
57475     maxText : "The date in this field must be equal to or before {0}",
57476     /**
57477      * @cfg {String} invalidText
57478      * The error text to display when the date in the field is invalid (defaults to
57479      * <tt>'{value} is not a valid date - it must be in the format {format}'</tt>).
57480      */
57481     invalidText : "{0} is not a valid date - it must be in the format {1}",
57482     /**
57483      * @cfg {String} triggerClass
57484      * An additional CSS class used to style the trigger button.  The trigger will always get the
57485      * class <tt>'x-form-trigger'</tt> and <tt>triggerClass</tt> will be <b>appended</b> if specified
57486      * (defaults to <tt>'x-form-date-trigger'</tt> which displays a calendar icon).
57487      */
57488     triggerClass : 'x-form-date-trigger',
57489     /**
57490      * @cfg {Boolean} showToday
57491      * <tt>false</tt> to hide the footer area of the DatePicker containing the Today button and disable
57492      * the keyboard handler for spacebar that selects the current date (defaults to <tt>true</tt>).
57493      */
57494     showToday : true,
57495     /**
57496      * @cfg {Date/String} minValue
57497      * The minimum allowed date. Can be either a Javascript date object or a string date in a
57498      * valid format (defaults to null).
57499      */
57500     /**
57501      * @cfg {Date/String} maxValue
57502      * The maximum allowed date. Can be either a Javascript date object or a string date in a
57503      * valid format (defaults to null).
57504      */
57505     /**
57506      * @cfg {Array} disabledDays
57507      * An array of days to disable, 0 based (defaults to null). Some examples:<pre><code>
57508 // disable Sunday and Saturday:
57509 disabledDays:  [0, 6]
57510 // disable weekdays:
57511 disabledDays: [1,2,3,4,5]
57512      * </code></pre>
57513      */
57514     /**
57515      * @cfg {Array} disabledDates
57516      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
57517      * expression so they are very powerful. Some examples:<pre><code>
57518 // disable these exact dates:
57519 disabledDates: ["03/08/2003", "09/16/2003"]
57520 // disable these days for every year:
57521 disabledDates: ["03/08", "09/16"]
57522 // only match the beginning (useful if you are using short years):
57523 disabledDates: ["^03/08"]
57524 // disable every day in March 2006:
57525 disabledDates: ["03/../2006"]
57526 // disable every day in every March:
57527 disabledDates: ["^03"]
57528      * </code></pre>
57529      * Note that the format of the dates included in the array should exactly match the {@link #format} config.
57530      * In order to support regular expressions, if you are using a {@link #format date format} that has "." in
57531      * it, you will have to escape the dot when restricting dates. For example: <tt>["03\\.08\\.03"]</tt>.
57532      */
57533     /**
57534      * @cfg {String/Object} autoCreate
57535      * A {@link Ext.DomHelper DomHelper element specification object}, or <tt>true</tt> for the default element
57536      * specification object:<pre><code>
57537      * autoCreate: {tag: "input", type: "text", size: "10", autocomplete: "off"}
57538      * </code></pre>
57539      */
57540
57541     // private
57542     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
57543
57544     initComponent : function(){
57545         Ext.form.DateField.superclass.initComponent.call(this);
57546
57547         this.addEvents(
57548             /**
57549              * @event select
57550              * Fires when a date is selected via the date picker.
57551              * @param {Ext.form.DateField} this
57552              * @param {Date} date The date that was selected
57553              */
57554             'select'
57555         );
57556
57557         if(Ext.isString(this.minValue)){
57558             this.minValue = this.parseDate(this.minValue);
57559         }
57560         if(Ext.isString(this.maxValue)){
57561             this.maxValue = this.parseDate(this.maxValue);
57562         }
57563         this.disabledDatesRE = null;
57564         this.initDisabledDays();
57565     },
57566     
57567     initEvents: function() {
57568         Ext.form.DateField.superclass.initEvents.call(this);
57569         this.keyNav = new Ext.KeyNav(this.el, {
57570             "down": function(e) {
57571                 this.onTriggerClick();
57572             },
57573             scope: this,
57574             forceKeyDown: true
57575         });
57576     },
57577
57578
57579     // private
57580     initDisabledDays : function(){
57581         if(this.disabledDates){
57582             var dd = this.disabledDates,
57583                 len = dd.length - 1, 
57584                 re = "(?:";
57585                 
57586             Ext.each(dd, function(d, i){
57587                 re += Ext.isDate(d) ? '^' + Ext.escapeRe(d.dateFormat(this.format)) + '$' : dd[i];
57588                 if(i != len){
57589                     re += '|';
57590                 }
57591             }, this);
57592             this.disabledDatesRE = new RegExp(re + ')');
57593         }
57594     },
57595
57596     /**
57597      * Replaces any existing disabled dates with new values and refreshes the DatePicker.
57598      * @param {Array} disabledDates An array of date strings (see the <tt>{@link #disabledDates}</tt> config
57599      * for details on supported values) used to disable a pattern of dates.
57600      */
57601     setDisabledDates : function(dd){
57602         this.disabledDates = dd;
57603         this.initDisabledDays();
57604         if(this.menu){
57605             this.menu.picker.setDisabledDates(this.disabledDatesRE);
57606         }
57607     },
57608
57609     /**
57610      * Replaces any existing disabled days (by index, 0-6) with new values and refreshes the DatePicker.
57611      * @param {Array} disabledDays An array of disabled day indexes. See the <tt>{@link #disabledDays}</tt>
57612      * config for details on supported values.
57613      */
57614     setDisabledDays : function(dd){
57615         this.disabledDays = dd;
57616         if(this.menu){
57617             this.menu.picker.setDisabledDays(dd);
57618         }
57619     },
57620
57621     /**
57622      * Replaces any existing <tt>{@link #minValue}</tt> with the new value and refreshes the DatePicker.
57623      * @param {Date} value The minimum date that can be selected
57624      */
57625     setMinValue : function(dt){
57626         this.minValue = (Ext.isString(dt) ? this.parseDate(dt) : dt);
57627         if(this.menu){
57628             this.menu.picker.setMinDate(this.minValue);
57629         }
57630     },
57631
57632     /**
57633      * Replaces any existing <tt>{@link #maxValue}</tt> with the new value and refreshes the DatePicker.
57634      * @param {Date} value The maximum date that can be selected
57635      */
57636     setMaxValue : function(dt){
57637         this.maxValue = (Ext.isString(dt) ? this.parseDate(dt) : dt);
57638         if(this.menu){
57639             this.menu.picker.setMaxDate(this.maxValue);
57640         }
57641     },
57642
57643     // private
57644     validateValue : function(value){
57645         value = this.formatDate(value);
57646         if(!Ext.form.DateField.superclass.validateValue.call(this, value)){
57647             return false;
57648         }
57649         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
57650              return true;
57651         }
57652         var svalue = value;
57653         value = this.parseDate(value);
57654         if(!value){
57655             this.markInvalid(String.format(this.invalidText, svalue, this.format));
57656             return false;
57657         }
57658         var time = value.getTime();
57659         if(this.minValue && time < this.minValue.getTime()){
57660             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
57661             return false;
57662         }
57663         if(this.maxValue && time > this.maxValue.getTime()){
57664             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
57665             return false;
57666         }
57667         if(this.disabledDays){
57668             var day = value.getDay();
57669             for(var i = 0; i < this.disabledDays.length; i++) {
57670                 if(day === this.disabledDays[i]){
57671                     this.markInvalid(this.disabledDaysText);
57672                     return false;
57673                 }
57674             }
57675         }
57676         var fvalue = this.formatDate(value);
57677         if(this.disabledDatesRE && this.disabledDatesRE.test(fvalue)){
57678             this.markInvalid(String.format(this.disabledDatesText, fvalue));
57679             return false;
57680         }
57681         return true;
57682     },
57683
57684     // private
57685     // Provides logic to override the default TriggerField.validateBlur which just returns true
57686     validateBlur : function(){
57687         return !this.menu || !this.menu.isVisible();
57688     },
57689
57690     /**
57691      * Returns the current date value of the date field.
57692      * @return {Date} The date value
57693      */
57694     getValue : function(){
57695         return this.parseDate(Ext.form.DateField.superclass.getValue.call(this)) || "";
57696     },
57697
57698     /**
57699      * Sets the value of the date field.  You can pass a date object or any string that can be
57700      * parsed into a valid date, using <tt>{@link #format}</tt> as the date format, according
57701      * to the same rules as {@link Date#parseDate} (the default format used is <tt>"m/d/Y"</tt>).
57702      * <br />Usage:
57703      * <pre><code>
57704 //All of these calls set the same date value (May 4, 2006)
57705
57706 //Pass a date object:
57707 var dt = new Date('5/4/2006');
57708 dateField.setValue(dt);
57709
57710 //Pass a date string (default format):
57711 dateField.setValue('05/04/2006');
57712
57713 //Pass a date string (custom format):
57714 dateField.format = 'Y-m-d';
57715 dateField.setValue('2006-05-04');
57716 </code></pre>
57717      * @param {String/Date} date The date or valid date string
57718      * @return {Ext.form.Field} this
57719      */
57720     setValue : function(date){
57721         return Ext.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
57722     },
57723
57724     // private
57725     parseDate : function(value){
57726         if(!value || Ext.isDate(value)){
57727             return value;
57728         }
57729         var v = Date.parseDate(value, this.format);
57730         if(!v && this.altFormats){
57731             if(!this.altFormatsArray){
57732                 this.altFormatsArray = this.altFormats.split("|");
57733             }
57734             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
57735                 v = Date.parseDate(value, this.altFormatsArray[i]);
57736             }
57737         }
57738         return v;
57739     },
57740
57741     // private
57742     onDestroy : function(){
57743                 Ext.destroy(this.menu, this.keyNav);
57744         Ext.form.DateField.superclass.onDestroy.call(this);
57745     },
57746
57747     // private
57748     formatDate : function(date){
57749         return Ext.isDate(date) ? date.dateFormat(this.format) : date;
57750     },
57751
57752     /**
57753      * @method onTriggerClick
57754      * @hide
57755      */
57756     // private
57757     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
57758     onTriggerClick : function(){
57759         if(this.disabled){
57760             return;
57761         }
57762         if(this.menu == null){
57763             this.menu = new Ext.menu.DateMenu({
57764                 hideOnClick: false,
57765                 focusOnSelect: false
57766             });
57767         }
57768         this.onFocus();
57769         Ext.apply(this.menu.picker,  {
57770             minDate : this.minValue,
57771             maxDate : this.maxValue,
57772             disabledDatesRE : this.disabledDatesRE,
57773             disabledDatesText : this.disabledDatesText,
57774             disabledDays : this.disabledDays,
57775             disabledDaysText : this.disabledDaysText,
57776             format : this.format,
57777             showToday : this.showToday,
57778             minText : String.format(this.minText, this.formatDate(this.minValue)),
57779             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
57780         });
57781         this.menu.picker.setValue(this.getValue() || new Date());
57782         this.menu.show(this.el, "tl-bl?");
57783         this.menuEvents('on');
57784     },
57785     
57786     //private
57787     menuEvents: function(method){
57788         this.menu[method]('select', this.onSelect, this);
57789         this.menu[method]('hide', this.onMenuHide, this);
57790         this.menu[method]('show', this.onFocus, this);
57791     },
57792     
57793     onSelect: function(m, d){
57794         this.setValue(d);
57795         this.fireEvent('select', this, d);
57796         this.menu.hide();
57797     },
57798     
57799     onMenuHide: function(){
57800         this.focus(false, 60);
57801         this.menuEvents('un');
57802     },
57803
57804     // private
57805     beforeBlur : function(){
57806         var v = this.parseDate(this.getRawValue());
57807         if(v){
57808             this.setValue(v);
57809         }
57810     }
57811
57812     /**
57813      * @cfg {Boolean} grow @hide
57814      */
57815     /**
57816      * @cfg {Number} growMin @hide
57817      */
57818     /**
57819      * @cfg {Number} growMax @hide
57820      */
57821     /**
57822      * @hide
57823      * @method autoSize
57824      */
57825 });
57826 Ext.reg('datefield', Ext.form.DateField);/**\r
57827  * @class Ext.form.DisplayField\r
57828  * @extends Ext.form.Field\r
57829  * A display-only text field which is not validated and not submitted.\r
57830  * @constructor\r
57831  * Creates a new DisplayField.\r
57832  * @param {Object} config Configuration options\r
57833  * @xtype displayfield\r
57834  */\r
57835 Ext.form.DisplayField = Ext.extend(Ext.form.Field,  {\r
57836     validationEvent : false,\r
57837     validateOnBlur : false,\r
57838     defaultAutoCreate : {tag: "div"},\r
57839     /**\r
57840      * @cfg {String} fieldClass The default CSS class for the field (defaults to <tt>"x-form-display-field"</tt>)\r
57841      */\r
57842     fieldClass : "x-form-display-field",\r
57843     /**\r
57844      * @cfg {Boolean} htmlEncode <tt>false</tt> to skip HTML-encoding the text when rendering it (defaults to\r
57845      * <tt>false</tt>). This might be useful if you want to include tags in the field's innerHTML rather than\r
57846      * rendering them as string literals per the default logic.\r
57847      */\r
57848     htmlEncode: false,\r
57849 \r
57850     // private\r
57851     initEvents : Ext.emptyFn,\r
57852 \r
57853     isValid : function(){\r
57854         return true;\r
57855     },\r
57856 \r
57857     validate : function(){\r
57858         return true;\r
57859     },\r
57860 \r
57861     getRawValue : function(){\r
57862         var v = this.rendered ? this.el.dom.innerHTML : Ext.value(this.value, '');\r
57863         if(v === this.emptyText){\r
57864             v = '';\r
57865         }\r
57866         if(this.htmlEncode){\r
57867             v = Ext.util.Format.htmlDecode(v);\r
57868         }\r
57869         return v;\r
57870     },\r
57871 \r
57872     getValue : function(){\r
57873         return this.getRawValue();\r
57874     },\r
57875     \r
57876     getName: function() {\r
57877         return this.name;\r
57878     },\r
57879 \r
57880     setRawValue : function(v){\r
57881         if(this.htmlEncode){\r
57882             v = Ext.util.Format.htmlEncode(v);\r
57883         }\r
57884         return this.rendered ? (this.el.dom.innerHTML = (Ext.isEmpty(v) ? '' : v)) : (this.value = v);\r
57885     },\r
57886 \r
57887     setValue : function(v){\r
57888         this.setRawValue(v);\r
57889         return this;\r
57890     }\r
57891     /** \r
57892      * @cfg {String} inputType \r
57893      * @hide\r
57894      */\r
57895     /** \r
57896      * @cfg {Boolean} disabled \r
57897      * @hide\r
57898      */\r
57899     /** \r
57900      * @cfg {Boolean} readOnly \r
57901      * @hide\r
57902      */\r
57903     /** \r
57904      * @cfg {Boolean} validateOnBlur \r
57905      * @hide\r
57906      */\r
57907     /** \r
57908      * @cfg {Number} validationDelay \r
57909      * @hide\r
57910      */\r
57911     /** \r
57912      * @cfg {String/Boolean} validationEvent \r
57913      * @hide\r
57914      */\r
57915 });\r
57916 \r
57917 Ext.reg('displayfield', Ext.form.DisplayField);\r
57918 /**
57919  * @class Ext.form.ComboBox
57920  * @extends Ext.form.TriggerField
57921  * <p>A combobox control with support for autocomplete, remote-loading, paging and many other features.</p>
57922  * <p>A ComboBox works in a similar manner to a traditional HTML &lt;select> field. The difference is
57923  * that to submit the {@link #valueField}, you must specify a {@link #hiddenName} to create a hidden input
57924  * field to hold the value of the valueField. The <i>{@link #displayField}</i> is shown in the text field
57925  * which is named according to the {@link #name}.</p>
57926  * <p><b><u>Events</u></b></p>
57927  * <p>To do something when something in ComboBox is selected, configure the select event:<pre><code>
57928 var cb = new Ext.form.ComboBox({
57929     // all of your config options
57930     listeners:{
57931          scope: yourScope,
57932          'select': yourFunction
57933     }
57934 });
57935
57936 // Alternatively, you can assign events after the object is created:
57937 var cb = new Ext.form.ComboBox(yourOptions);
57938 cb.on('select', yourFunction, yourScope);
57939  * </code></pre></p>
57940  *
57941  * <p><b><u>ComboBox in Grid</u></b></p>
57942  * <p>If using a ComboBox in an {@link Ext.grid.EditorGridPanel Editor Grid} a {@link Ext.grid.Column#renderer renderer}
57943  * will be needed to show the displayField when the editor is not active.  Set up the renderer manually, or implement
57944  * a reusable render, for example:<pre><code>
57945 // create reusable renderer
57946 Ext.util.Format.comboRenderer = function(combo){
57947     return function(value){
57948         var record = combo.findRecord(combo.{@link #valueField}, value);
57949         return record ? record.get(combo.{@link #displayField}) : combo.{@link #valueNotFoundText};
57950     }
57951 }
57952
57953 // create the combo instance
57954 var combo = new Ext.form.ComboBox({
57955     {@link #typeAhead}: true,
57956     {@link #triggerAction}: 'all',
57957     {@link #lazyRender}:true,
57958     {@link #mode}: 'local',
57959     {@link #store}: new Ext.data.ArrayStore({
57960         id: 0,
57961         fields: [
57962             'myId',
57963             'displayText'
57964         ],
57965         data: [[1, 'item1'], [2, 'item2']]
57966     }),
57967     {@link #valueField}: 'myId',
57968     {@link #displayField}: 'displayText'
57969 });
57970
57971 // snippet of column model used within grid
57972 var cm = new Ext.grid.ColumnModel([{
57973        ...
57974     },{
57975        header: "Some Header",
57976        dataIndex: 'whatever',
57977        width: 130,
57978        editor: combo, // specify reference to combo instance
57979        renderer: Ext.util.Format.comboRenderer(combo) // pass combo instance to reusable renderer
57980     },
57981     ...
57982 ]);
57983  * </code></pre></p>
57984  *
57985  * <p><b><u>Filtering</u></b></p>
57986  * <p>A ComboBox {@link #doQuery uses filtering itself}, for information about filtering the ComboBox
57987  * store manually see <tt>{@link #lastQuery}</tt>.</p>
57988  * @constructor
57989  * Create a new ComboBox.
57990  * @param {Object} config Configuration options
57991  * @xtype combo
57992  */
57993 Ext.form.ComboBox = Ext.extend(Ext.form.TriggerField, {
57994     /**
57995      * @cfg {Mixed} transform The id, DOM node or element of an existing HTML SELECT to convert to a ComboBox.
57996      * Note that if you specify this and the combo is going to be in an {@link Ext.form.BasicForm} or
57997      * {@link Ext.form.FormPanel}, you must also set <tt>{@link #lazyRender} = true</tt>.
57998      */
57999     /**
58000      * @cfg {Boolean} lazyRender <tt>true</tt> to prevent the ComboBox from rendering until requested
58001      * (should always be used when rendering into an {@link Ext.Editor} (e.g. {@link Ext.grid.EditorGridPanel Grids}),
58002      * defaults to <tt>false</tt>).
58003      */
58004     /**
58005      * @cfg {String/Object} autoCreate <p>A {@link Ext.DomHelper DomHelper} element spec, or <tt>true</tt> for a default
58006      * element spec. Used to create the {@link Ext.Component#getEl Element} which will encapsulate this Component.
58007      * See <tt>{@link Ext.Component#autoEl autoEl}</tt> for details.  Defaults to:</p>
58008      * <pre><code>{tag: "input", type: "text", size: "24", autocomplete: "off"}</code></pre>
58009      */
58010     /**
58011      * @cfg {Ext.data.Store/Array} store The data source to which this combo is bound (defaults to <tt>undefined</tt>).
58012      * Acceptable values for this property are:
58013      * <div class="mdetail-params"><ul>
58014      * <li><b>any {@link Ext.data.Store Store} subclass</b></li>
58015      * <li><b>an Array</b> : Arrays will be converted to a {@link Ext.data.ArrayStore} internally,
58016      * automatically generating {@link Ext.data.Field#name field names} to work with all data components.
58017      * <div class="mdetail-params"><ul>
58018      * <li><b>1-dimensional array</b> : (e.g., <tt>['Foo','Bar']</tt>)<div class="sub-desc">
58019      * A 1-dimensional array will automatically be expanded (each array item will be used for both the combo
58020      * {@link #valueField} and {@link #displayField})</div></li>
58021      * <li><b>2-dimensional array</b> : (e.g., <tt>[['f','Foo'],['b','Bar']]</tt>)<div class="sub-desc">
58022      * For a multi-dimensional array, the value in index 0 of each item will be assumed to be the combo
58023      * {@link #valueField}, while the value at index 1 is assumed to be the combo {@link #displayField}.
58024      * </div></li></ul></div></li></ul></div>
58025      * <p>See also <tt>{@link #mode}</tt>.</p>
58026      */
58027     /**
58028      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
58029      * the dropdown list (defaults to undefined, with no header element)
58030      */
58031
58032     // private
58033     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
58034     /**
58035      * @cfg {Number} listWidth The width (used as a parameter to {@link Ext.Element#setWidth}) of the dropdown
58036      * list (defaults to the width of the ComboBox field).  See also <tt>{@link #minListWidth}
58037      */
58038     /**
58039      * @cfg {String} displayField The underlying {@link Ext.data.Field#name data field name} to bind to this
58040      * ComboBox (defaults to undefined if <tt>{@link #mode} = 'remote'</tt> or <tt>'field1'</tt> if
58041      * {@link #transform transforming a select} or if the {@link #store field name is autogenerated based on
58042      * the store configuration}).
58043      * <p>See also <tt>{@link #valueField}</tt>.</p>
58044      * <p><b>Note</b>: if using a ComboBox in an {@link Ext.grid.EditorGridPanel Editor Grid} a
58045      * {@link Ext.grid.Column#renderer renderer} will be needed to show the displayField when the editor is not
58046      * active.</p>
58047      */
58048     /**
58049      * @cfg {String} valueField The underlying {@link Ext.data.Field#name data value name} to bind to this
58050      * ComboBox (defaults to undefined if <tt>{@link #mode} = 'remote'</tt> or <tt>'field2'</tt> if
58051      * {@link #transform transforming a select} or if the {@link #store field name is autogenerated based on
58052      * the store configuration}).
58053      * <p><b>Note</b>: use of a <tt>valueField</tt> requires the user to make a selection in order for a value to be
58054      * mapped.  See also <tt>{@link #hiddenName}</tt>, <tt>{@link #hiddenValue}</tt>, and <tt>{@link #displayField}</tt>.</p>
58055      */
58056     /**
58057      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
58058      * field's data value (defaults to the underlying DOM element's name). Required for the combo's value to automatically
58059      * post during a form submission.  See also {@link #valueField}.
58060      * <p><b>Note</b>: the hidden field's id will also default to this name if {@link #hiddenId} is not specified.
58061      * The ComboBox {@link Ext.Component#id id} and the <tt>{@link #hiddenId}</tt> <b>should be different</b>, since
58062      * no two DOM nodes should share the same id.  So, if the ComboBox <tt>{@link Ext.form.Field#name name}</tt> and
58063      * <tt>hiddenName</tt> are the same, you should specify a unique <tt>{@link #hiddenId}</tt>.</p>
58064      */
58065     /**
58066      * @cfg {String} hiddenId If <tt>{@link #hiddenName}</tt> is specified, <tt>hiddenId</tt> can also be provided
58067      * to give the hidden field a unique id (defaults to the <tt>{@link #hiddenName}</tt>).  The <tt>hiddenId</tt>
58068      * and combo {@link Ext.Component#id id} should be different, since no two DOM
58069      * nodes should share the same id.
58070      */
58071     /**
58072      * @cfg {String} hiddenValue Sets the initial value of the hidden field if {@link #hiddenName} is
58073      * specified to contain the selected {@link #valueField}, from the Store. Defaults to the configured
58074      * <tt>{@link Ext.form.Field#value value}</tt>.
58075      */
58076     /**
58077      * @cfg {String} listClass The CSS class to add to the predefined <tt>'x-combo-list'</tt> class
58078      * applied the dropdown list element (defaults to '').
58079      */
58080     listClass : '',
58081     /**
58082      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list
58083      * (defaults to <tt>'x-combo-selected'</tt>)
58084      */
58085     selectedClass : 'x-combo-selected',
58086     /**
58087      * @cfg {String} listEmptyText The empty text to display in the data view if no items are found.
58088      * (defaults to '')
58089      */
58090     listEmptyText: '',
58091     /**
58092      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always
58093      * get the class <tt>'x-form-trigger'</tt> and <tt>triggerClass</tt> will be <b>appended</b> if specified
58094      * (defaults to <tt>'x-form-arrow-trigger'</tt> which displays a downward arrow icon).
58095      */
58096     triggerClass : 'x-form-arrow-trigger',
58097     /**
58098      * @cfg {Boolean/String} shadow <tt>true</tt> or <tt>"sides"</tt> for the default effect, <tt>"frame"</tt> for
58099      * 4-way shadow, and <tt>"drop"</tt> for bottom-right
58100      */
58101     shadow : 'sides',
58102     /**
58103      * @cfg {String/Array} listAlign A valid anchor position value. See <tt>{@link Ext.Element#alignTo}</tt> for details
58104      * on supported anchor positions and offsets. To specify x/y offsets as well, this value
58105      * may be specified as an Array of <tt>{@link Ext.Element#alignTo}</tt> method arguments.</p>
58106      * <pre><code>[ 'tl-bl?', [6,0] ]</code></pre>(defaults to <tt>'tl-bl?'</tt>)
58107      */
58108     listAlign : 'tl-bl?',
58109     /**
58110      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown
58111      * (defaults to <tt>300</tt>)
58112      */
58113     maxHeight : 300,
58114     /**
58115      * @cfg {Number} minHeight The minimum height in pixels of the dropdown list when the list is constrained by its
58116      * distance to the viewport edges (defaults to <tt>90</tt>)
58117      */
58118     minHeight : 90,
58119     /**
58120      * @cfg {String} triggerAction The action to execute when the trigger is clicked.
58121      * <div class="mdetail-params"><ul>
58122      * <li><b><tt>'query'</tt></b> : <b>Default</b>
58123      * <p class="sub-desc">{@link #doQuery run the query} using the {@link Ext.form.Field#getRawValue raw value}.</p></li>
58124      * <li><b><tt>'all'</tt></b> :
58125      * <p class="sub-desc">{@link #doQuery run the query} specified by the <tt>{@link #allQuery}</tt> config option</p></li>
58126      * </ul></div>
58127      * <p>See also <code>{@link #queryParam}</code>.</p>
58128      */
58129     triggerAction : 'query',
58130     /**
58131      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and
58132      * {@link #typeAhead} activate (defaults to <tt>4</tt> if <tt>{@link #mode} = 'remote'</tt> or <tt>0</tt> if
58133      * <tt>{@link #mode} = 'local'</tt>, does not apply if
58134      * <tt>{@link Ext.form.TriggerField#editable editable} = false</tt>).
58135      */
58136     minChars : 4,
58137     /**
58138      * @cfg {Boolean} autoSelect <tt>true</tt> to select the first result gathered by the data store (defaults
58139      * to <tt>true</tt>).  A false value would require a manual selection from the dropdown list to set the components value
58140      * unless the value of ({@link #typeAheadDelay}) were true.
58141      */
58142     autoSelect : true,
58143     /**
58144      * @cfg {Boolean} typeAhead <tt>true</tt> to populate and autoselect the remainder of the text being
58145      * typed after a configurable delay ({@link #typeAheadDelay}) if it matches a known value (defaults
58146      * to <tt>false</tt>)
58147      */
58148     typeAhead : false,
58149     /**
58150      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and
58151      * sending the query to filter the dropdown list (defaults to <tt>500</tt> if <tt>{@link #mode} = 'remote'</tt>
58152      * or <tt>10</tt> if <tt>{@link #mode} = 'local'</tt>)
58153      */
58154     queryDelay : 500,
58155     /**
58156      * @cfg {Number} pageSize If greater than <tt>0</tt>, a {@link Ext.PagingToolbar} is displayed in the
58157      * footer of the dropdown list and the {@link #doQuery filter queries} will execute with page start and
58158      * {@link Ext.PagingToolbar#pageSize limit} parameters. Only applies when <tt>{@link #mode} = 'remote'</tt>
58159      * (defaults to <tt>0</tt>).
58160      */
58161     pageSize : 0,
58162     /**
58163      * @cfg {Boolean} selectOnFocus <tt>true</tt> to select any existing text in the field immediately on focus.
58164      * Only applies when <tt>{@link Ext.form.TriggerField#editable editable} = true</tt> (defaults to
58165      * <tt>false</tt>).
58166      */
58167     selectOnFocus : false,
58168     /**
58169      * @cfg {String} queryParam Name of the query ({@link Ext.data.Store#baseParam baseParam} name for the store)
58170      * as it will be passed on the querystring (defaults to <tt>'query'</tt>)
58171      */
58172     queryParam : 'query',
58173     /**
58174      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
58175      * when <tt>{@link #mode} = 'remote'</tt> (defaults to <tt>'Loading...'</tt>)
58176      */
58177     loadingText : 'Loading...',
58178     /**
58179      * @cfg {Boolean} resizable <tt>true</tt> to add a resize handle to the bottom of the dropdown list
58180      * (creates an {@link Ext.Resizable} with 'se' {@link Ext.Resizable#pinned pinned} handles).
58181      * Defaults to <tt>false</tt>.
58182      */
58183     resizable : false,
58184     /**
58185      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if
58186      * <tt>{@link #resizable} = true</tt> (defaults to <tt>8</tt>)
58187      */
58188     handleHeight : 8,
58189     /**
58190      * @cfg {String} allQuery The text query to send to the server to return all records for the list
58191      * with no filtering (defaults to '')
58192      */
58193     allQuery: '',
58194     /**
58195      * @cfg {String} mode Acceptable values are:
58196      * <div class="mdetail-params"><ul>
58197      * <li><b><tt>'remote'</tt></b> : <b>Default</b>
58198      * <p class="sub-desc">Automatically loads the <tt>{@link #store}</tt> the <b>first</b> time the trigger
58199      * is clicked. If you do not want the store to be automatically loaded the first time the trigger is
58200      * clicked, set to <tt>'local'</tt> and manually load the store.  To force a requery of the store
58201      * <b>every</b> time the trigger is clicked see <tt>{@link #lastQuery}</tt>.</p></li>
58202      * <li><b><tt>'local'</tt></b> :
58203      * <p class="sub-desc">ComboBox loads local data</p>
58204      * <pre><code>
58205 var combo = new Ext.form.ComboBox({
58206     renderTo: document.body,
58207     mode: 'local',
58208     store: new Ext.data.ArrayStore({
58209         id: 0,
58210         fields: [
58211             'myId',  // numeric value is the key
58212             'displayText'
58213         ],
58214         data: [[1, 'item1'], [2, 'item2']]  // data is local
58215     }),
58216     valueField: 'myId',
58217     displayField: 'displayText',
58218     triggerAction: 'all'
58219 });
58220      * </code></pre></li>
58221      * </ul></div>
58222      */
58223     mode: 'remote',
58224     /**
58225      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to <tt>70</tt>, will
58226      * be ignored if <tt>{@link #listWidth}</tt> has a higher value)
58227      */
58228     minListWidth : 70,
58229     /**
58230      * @cfg {Boolean} forceSelection <tt>true</tt> to restrict the selected value to one of the values in the list,
58231      * <tt>false</tt> to allow the user to set arbitrary text into the field (defaults to <tt>false</tt>)
58232      */
58233     forceSelection : false,
58234     /**
58235      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
58236      * if <tt>{@link #typeAhead} = true</tt> (defaults to <tt>250</tt>)
58237      */
58238     typeAheadDelay : 250,
58239     /**
58240      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
58241      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined). If this
58242      * default text is used, it means there is no value set and no validation will occur on this field.
58243      */
58244
58245     /**
58246      * @cfg {Boolean} lazyInit <tt>true</tt> to not initialize the list for this combo until the field is focused
58247      * (defaults to <tt>true</tt>)
58248      */
58249     lazyInit : true,
58250
58251     /**
58252      * @cfg {Boolean} clearFilterOnReset <tt>true</tt> to clear any filters on the store (when in local mode) when reset is called
58253      * (defaults to <tt>true</tt>)
58254      */
58255     clearFilterOnReset : true,
58256
58257     /**
58258      * @cfg {Boolean} submitValue False to clear the name attribute on the field so that it is not submitted during a form post.
58259      * If a hiddenName is specified, setting this to true will cause both the hidden field and the element to be submitted.
58260      * Defaults to <tt>undefined</tt>.
58261      */
58262     submitValue: undefined,
58263
58264     /**
58265      * The value of the match string used to filter the store. Delete this property to force a requery.
58266      * Example use:
58267      * <pre><code>
58268 var combo = new Ext.form.ComboBox({
58269     ...
58270     mode: 'remote',
58271     ...
58272     listeners: {
58273         // delete the previous query in the beforequery event or set
58274         // combo.lastQuery = null (this will reload the store the next time it expands)
58275         beforequery: function(qe){
58276             delete qe.combo.lastQuery;
58277         }
58278     }
58279 });
58280      * </code></pre>
58281      * To make sure the filter in the store is not cleared the first time the ComboBox trigger is used
58282      * configure the combo with <tt>lastQuery=''</tt>. Example use:
58283      * <pre><code>
58284 var combo = new Ext.form.ComboBox({
58285     ...
58286     mode: 'local',
58287     triggerAction: 'all',
58288     lastQuery: ''
58289 });
58290      * </code></pre>
58291      * @property lastQuery
58292      * @type String
58293      */
58294
58295     // private
58296     initComponent : function(){
58297         Ext.form.ComboBox.superclass.initComponent.call(this);
58298         this.addEvents(
58299             /**
58300              * @event expand
58301              * Fires when the dropdown list is expanded
58302              * @param {Ext.form.ComboBox} combo This combo box
58303              */
58304             'expand',
58305             /**
58306              * @event collapse
58307              * Fires when the dropdown list is collapsed
58308              * @param {Ext.form.ComboBox} combo This combo box
58309              */
58310             'collapse',
58311
58312             /**
58313              * @event beforeselect
58314              * Fires before a list item is selected. Return false to cancel the selection.
58315              * @param {Ext.form.ComboBox} combo This combo box
58316              * @param {Ext.data.Record} record The data record returned from the underlying store
58317              * @param {Number} index The index of the selected item in the dropdown list
58318              */
58319             'beforeselect',
58320             /**
58321              * @event select
58322              * Fires when a list item is selected
58323              * @param {Ext.form.ComboBox} combo This combo box
58324              * @param {Ext.data.Record} record The data record returned from the underlying store
58325              * @param {Number} index The index of the selected item in the dropdown list
58326              */
58327             'select',
58328             /**
58329              * @event beforequery
58330              * Fires before all queries are processed. Return false to cancel the query or set the queryEvent's
58331              * cancel property to true.
58332              * @param {Object} queryEvent An object that has these properties:<ul>
58333              * <li><code>combo</code> : Ext.form.ComboBox <div class="sub-desc">This combo box</div></li>
58334              * <li><code>query</code> : String <div class="sub-desc">The query</div></li>
58335              * <li><code>forceAll</code> : Boolean <div class="sub-desc">True to force "all" query</div></li>
58336              * <li><code>cancel</code> : Boolean <div class="sub-desc">Set to true to cancel the query</div></li>
58337              * </ul>
58338              */
58339             'beforequery'
58340         );
58341         if(this.transform){
58342             var s = Ext.getDom(this.transform);
58343             if(!this.hiddenName){
58344                 this.hiddenName = s.name;
58345             }
58346             if(!this.store){
58347                 this.mode = 'local';
58348                 var d = [], opts = s.options;
58349                 for(var i = 0, len = opts.length;i < len; i++){
58350                     var o = opts[i],
58351                         value = (o.hasAttribute ? o.hasAttribute('value') : o.getAttributeNode('value').specified) ? o.value : o.text;
58352                     if(o.selected && Ext.isEmpty(this.value, true)) {
58353                         this.value = value;
58354                     }
58355                     d.push([value, o.text]);
58356                 }
58357                 this.store = new Ext.data.ArrayStore({
58358                     'id': 0,
58359                     fields: ['value', 'text'],
58360                     data : d,
58361                     autoDestroy: true
58362                 });
58363                 this.valueField = 'value';
58364                 this.displayField = 'text';
58365             }
58366             s.name = Ext.id(); // wipe out the name in case somewhere else they have a reference
58367             if(!this.lazyRender){
58368                 this.target = true;
58369                 this.el = Ext.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
58370                 this.render(this.el.parentNode, s);
58371             }
58372             Ext.removeNode(s);
58373         }
58374         //auto-configure store from local array data
58375         else if(this.store){
58376             this.store = Ext.StoreMgr.lookup(this.store);
58377             if(this.store.autoCreated){
58378                 this.displayField = this.valueField = 'field1';
58379                 if(!this.store.expandData){
58380                     this.displayField = 'field2';
58381                 }
58382                 this.mode = 'local';
58383             }
58384         }
58385
58386         this.selectedIndex = -1;
58387         if(this.mode == 'local'){
58388             if(!Ext.isDefined(this.initialConfig.queryDelay)){
58389                 this.queryDelay = 10;
58390             }
58391             if(!Ext.isDefined(this.initialConfig.minChars)){
58392                 this.minChars = 0;
58393             }
58394         }
58395     },
58396
58397     // private
58398     onRender : function(ct, position){
58399         if(this.hiddenName && !Ext.isDefined(this.submitValue)){
58400             this.submitValue = false;
58401         }
58402         Ext.form.ComboBox.superclass.onRender.call(this, ct, position);
58403         if(this.hiddenName){
58404             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName,
58405                     id: (this.hiddenId||this.hiddenName)}, 'before', true);
58406
58407         }
58408         if(Ext.isGecko){
58409             this.el.dom.setAttribute('autocomplete', 'off');
58410         }
58411
58412         if(!this.lazyInit){
58413             this.initList();
58414         }else{
58415             this.on('focus', this.initList, this, {single: true});
58416         }
58417     },
58418
58419     // private
58420     initValue : function(){
58421         Ext.form.ComboBox.superclass.initValue.call(this);
58422         if(this.hiddenField){
58423             this.hiddenField.value =
58424                 Ext.value(Ext.isDefined(this.hiddenValue) ? this.hiddenValue : this.value, '');
58425         }
58426     },
58427
58428     // private
58429     initList : function(){
58430         if(!this.list){
58431             var cls = 'x-combo-list',
58432                 listParent = Ext.getDom(this.getListParent() || Ext.getBody()),
58433                 zindex = parseInt(Ext.fly(listParent).getStyle('z-index') ,10);
58434
58435             if (this.ownerCt && !zindex){
58436                 this.findParentBy(function(ct){
58437                     zindex = parseInt(ct.getPositionEl().getStyle('z-index'), 10);
58438                     return !!zindex;
58439                 });
58440             }
58441
58442             this.list = new Ext.Layer({
58443                 parentEl: listParent,
58444                 shadow: this.shadow,
58445                 cls: [cls, this.listClass].join(' '),
58446                 constrain:false,
58447                 zindex: (zindex || 12000) + 5
58448             });
58449
58450             var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
58451             this.list.setSize(lw, 0);
58452             this.list.swallowEvent('mousewheel');
58453             this.assetHeight = 0;
58454             if(this.syncFont !== false){
58455                 this.list.setStyle('font-size', this.el.getStyle('font-size'));
58456             }
58457             if(this.title){
58458                 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
58459                 this.assetHeight += this.header.getHeight();
58460             }
58461
58462             this.innerList = this.list.createChild({cls:cls+'-inner'});
58463             this.mon(this.innerList, 'mouseover', this.onViewOver, this);
58464             this.mon(this.innerList, 'mousemove', this.onViewMove, this);
58465             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
58466
58467             if(this.pageSize){
58468                 this.footer = this.list.createChild({cls:cls+'-ft'});
58469                 this.pageTb = new Ext.PagingToolbar({
58470                     store: this.store,
58471                     pageSize: this.pageSize,
58472                     renderTo:this.footer
58473                 });
58474                 this.assetHeight += this.footer.getHeight();
58475             }
58476
58477             if(!this.tpl){
58478                 /**
58479                 * @cfg {String/Ext.XTemplate} tpl <p>The template string, or {@link Ext.XTemplate} instance to
58480                 * use to display each item in the dropdown list. The dropdown list is displayed in a
58481                 * DataView. See {@link #view}.</p>
58482                 * <p>The default template string is:</p><pre><code>
58483                   '&lt;tpl for=".">&lt;div class="x-combo-list-item">{' + this.displayField + '}&lt;/div>&lt;/tpl>'
58484                 * </code></pre>
58485                 * <p>Override the default value to create custom UI layouts for items in the list.
58486                 * For example:</p><pre><code>
58487                   '&lt;tpl for=".">&lt;div ext:qtip="{state}. {nick}" class="x-combo-list-item">{state}&lt;/div>&lt;/tpl>'
58488                 * </code></pre>
58489                 * <p>The template <b>must</b> contain one or more substitution parameters using field
58490                 * names from the Combo's</b> {@link #store Store}. In the example above an
58491                 * <pre>ext:qtip</pre> attribute is added to display other fields from the Store.</p>
58492                 * <p>To preserve the default visual look of list items, add the CSS class name
58493                 * <pre>x-combo-list-item</pre> to the template's container element.</p>
58494                 * <p>Also see {@link #itemSelector} for additional details.</p>
58495                 */
58496                 this.tpl = '<tpl for="."><div class="'+cls+'-item">{' + this.displayField + '}</div></tpl>';
58497                 /**
58498                  * @cfg {String} itemSelector
58499                  * <p>A simple CSS selector (e.g. div.some-class or span:first-child) that will be
58500                  * used to determine what nodes the {@link #view Ext.DataView} which handles the dropdown
58501                  * display will be working with.</p>
58502                  * <p><b>Note</b>: this setting is <b>required</b> if a custom XTemplate has been
58503                  * specified in {@link #tpl} which assigns a class other than <pre>'x-combo-list-item'</pre>
58504                  * to dropdown list items</b>
58505                  */
58506             }
58507
58508             /**
58509             * The {@link Ext.DataView DataView} used to display the ComboBox's options.
58510             * @type Ext.DataView
58511             */
58512             this.view = new Ext.DataView({
58513                 applyTo: this.innerList,
58514                 tpl: this.tpl,
58515                 singleSelect: true,
58516                 selectedClass: this.selectedClass,
58517                 itemSelector: this.itemSelector || '.' + cls + '-item',
58518                 emptyText: this.listEmptyText,
58519                 deferEmptyText: false
58520             });
58521
58522             this.mon(this.view, {
58523                 containerclick : this.onViewClick,
58524                 click : this.onViewClick,
58525                 scope :this
58526             });
58527
58528             this.bindStore(this.store, true);
58529
58530             if(this.resizable){
58531                 this.resizer = new Ext.Resizable(this.list,  {
58532                    pinned:true, handles:'se'
58533                 });
58534                 this.mon(this.resizer, 'resize', function(r, w, h){
58535                     this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
58536                     this.listWidth = w;
58537                     this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
58538                     this.restrictHeight();
58539                 }, this);
58540
58541                 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
58542             }
58543         }
58544     },
58545
58546     /**
58547      * <p>Returns the element used to house this ComboBox's pop-up list. Defaults to the document body.</p>
58548      * A custom implementation may be provided as a configuration option if the floating list needs to be rendered
58549      * to a different Element. An example might be rendering the list inside a Menu so that clicking
58550      * the list does not hide the Menu:<pre><code>
58551 var store = new Ext.data.ArrayStore({
58552     autoDestroy: true,
58553     fields: ['initials', 'fullname'],
58554     data : [
58555         ['FF', 'Fred Flintstone'],
58556         ['BR', 'Barney Rubble']
58557     ]
58558 });
58559
58560 var combo = new Ext.form.ComboBox({
58561     store: store,
58562     displayField: 'fullname',
58563     emptyText: 'Select a name...',
58564     forceSelection: true,
58565     getListParent: function() {
58566         return this.el.up('.x-menu');
58567     },
58568     iconCls: 'no-icon', //use iconCls if placing within menu to shift to right side of menu
58569     mode: 'local',
58570     selectOnFocus: true,
58571     triggerAction: 'all',
58572     typeAhead: true,
58573     width: 135
58574 });
58575
58576 var menu = new Ext.menu.Menu({
58577     id: 'mainMenu',
58578     items: [
58579         combo // A Field in a Menu
58580     ]
58581 });
58582 </code></pre>
58583      */
58584     getListParent : function() {
58585         return document.body;
58586     },
58587
58588     /**
58589      * Returns the store associated with this combo.
58590      * @return {Ext.data.Store} The store
58591      */
58592     getStore : function(){
58593         return this.store;
58594     },
58595
58596     // private
58597     bindStore : function(store, initial){
58598         if(this.store && !initial){
58599             if(this.store !== store && this.store.autoDestroy){
58600                 this.store.destroy();
58601             }else{
58602                 this.store.un('beforeload', this.onBeforeLoad, this);
58603                 this.store.un('load', this.onLoad, this);
58604                 this.store.un('exception', this.collapse, this);
58605             }
58606             if(!store){
58607                 this.store = null;
58608                 if(this.view){
58609                     this.view.bindStore(null);
58610                 }
58611                 if(this.pageTb){
58612                     this.pageTb.bindStore(null);
58613                 }
58614             }
58615         }
58616         if(store){
58617             if(!initial) {
58618                 this.lastQuery = null;
58619                 if(this.pageTb) {
58620                     this.pageTb.bindStore(store);
58621                 }
58622             }
58623
58624             this.store = Ext.StoreMgr.lookup(store);
58625             this.store.on({
58626                 scope: this,
58627                 beforeload: this.onBeforeLoad,
58628                 load: this.onLoad,
58629                 exception: this.collapse
58630             });
58631
58632             if(this.view){
58633                 this.view.bindStore(store);
58634             }
58635         }
58636     },
58637
58638     reset : function(){
58639         Ext.form.ComboBox.superclass.reset.call(this);
58640         if(this.clearFilterOnReset && this.mode == 'local'){
58641             this.store.clearFilter();
58642         }
58643     },
58644
58645     // private
58646     initEvents : function(){
58647         Ext.form.ComboBox.superclass.initEvents.call(this);
58648
58649
58650         this.keyNav = new Ext.KeyNav(this.el, {
58651             "up" : function(e){
58652                 this.inKeyMode = true;
58653                 this.selectPrev();
58654             },
58655
58656             "down" : function(e){
58657                 if(!this.isExpanded()){
58658                     this.onTriggerClick();
58659                 }else{
58660                     this.inKeyMode = true;
58661                     this.selectNext();
58662                 }
58663             },
58664
58665             "enter" : function(e){
58666                 this.onViewClick();
58667             },
58668
58669             "esc" : function(e){
58670                 this.collapse();
58671             },
58672
58673             "tab" : function(e){
58674                 this.collapse();
58675                 return true;
58676             },
58677
58678             scope : this,
58679
58680             doRelay : function(e, h, hname){
58681                 if(hname == 'down' || this.scope.isExpanded()){
58682                     // this MUST be called before ComboBox#fireKey()
58683                     var relay = Ext.KeyNav.prototype.doRelay.apply(this, arguments);
58684                     if(!Ext.isIE && Ext.EventManager.useKeydown){
58685                         // call Combo#fireKey() for browsers which use keydown event (except IE)
58686                         this.scope.fireKey(e);
58687                     }
58688                     return relay;
58689                 }
58690                 return true;
58691             },
58692
58693             forceKeyDown : true,
58694             defaultEventAction: 'stopEvent'
58695         });
58696         this.queryDelay = Math.max(this.queryDelay || 10,
58697                 this.mode == 'local' ? 10 : 250);
58698         this.dqTask = new Ext.util.DelayedTask(this.initQuery, this);
58699         if(this.typeAhead){
58700             this.taTask = new Ext.util.DelayedTask(this.onTypeAhead, this);
58701         }
58702         if(!this.enableKeyEvents){
58703             this.mon(this.el, 'keyup', this.onKeyUp, this);
58704         }
58705     },
58706
58707
58708     // private
58709     onDestroy : function(){
58710         if (this.dqTask){
58711             this.dqTask.cancel();
58712             this.dqTask = null;
58713         }
58714         this.bindStore(null);
58715         Ext.destroy(
58716             this.resizer,
58717             this.view,
58718             this.pageTb,
58719             this.list
58720         );
58721         Ext.destroyMembers(this, 'hiddenField');
58722         Ext.form.ComboBox.superclass.onDestroy.call(this);
58723     },
58724
58725     // private
58726     fireKey : function(e){
58727         if (!this.isExpanded()) {
58728             Ext.form.ComboBox.superclass.fireKey.call(this, e);
58729         }
58730     },
58731
58732     // private
58733     onResize : function(w, h){
58734         Ext.form.ComboBox.superclass.onResize.apply(this, arguments);
58735         if(this.isVisible() && this.list){
58736             this.doResize(w);
58737         }else{
58738             this.bufferSize = w;
58739         }
58740     },
58741
58742     doResize: function(w){
58743         if(!Ext.isDefined(this.listWidth)){
58744             var lw = Math.max(w, this.minListWidth);
58745             this.list.setWidth(lw);
58746             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
58747         }
58748     },
58749
58750     // private
58751     onEnable : function(){
58752         Ext.form.ComboBox.superclass.onEnable.apply(this, arguments);
58753         if(this.hiddenField){
58754             this.hiddenField.disabled = false;
58755         }
58756     },
58757
58758     // private
58759     onDisable : function(){
58760         Ext.form.ComboBox.superclass.onDisable.apply(this, arguments);
58761         if(this.hiddenField){
58762             this.hiddenField.disabled = true;
58763         }
58764     },
58765
58766     // private
58767     onBeforeLoad : function(){
58768         if(!this.hasFocus){
58769             return;
58770         }
58771         this.innerList.update(this.loadingText ?
58772                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
58773         this.restrictHeight();
58774         this.selectedIndex = -1;
58775     },
58776
58777     // private
58778     onLoad : function(){
58779         if(!this.hasFocus){
58780             return;
58781         }
58782         if(this.store.getCount() > 0 || this.listEmptyText){
58783             this.expand();
58784             this.restrictHeight();
58785             if(this.lastQuery == this.allQuery){
58786                 if(this.editable){
58787                     this.el.dom.select();
58788                 }
58789
58790                 if(this.autoSelect !== false && !this.selectByValue(this.value, true)){
58791                     this.select(0, true);
58792                 }
58793             }else{
58794                 if(this.autoSelect !== false){
58795                     this.selectNext();
58796                 }
58797                 if(this.typeAhead && this.lastKey != Ext.EventObject.BACKSPACE && this.lastKey != Ext.EventObject.DELETE){
58798                     this.taTask.delay(this.typeAheadDelay);
58799                 }
58800             }
58801         }else{
58802             this.collapse();
58803         }
58804
58805     },
58806
58807     // private
58808     onTypeAhead : function(){
58809         if(this.store.getCount() > 0){
58810             var r = this.store.getAt(0);
58811             var newValue = r.data[this.displayField];
58812             var len = newValue.length;
58813             var selStart = this.getRawValue().length;
58814             if(selStart != len){
58815                 this.setRawValue(newValue);
58816                 this.selectText(selStart, newValue.length);
58817             }
58818         }
58819     },
58820
58821     // private
58822     assertValue  : function(){
58823
58824         var val = this.getRawValue(),
58825             rec = this.findRecord(this.displayField, val);
58826
58827         if(!rec && this.forceSelection){
58828             if(val.length > 0 && val != this.emptyText){
58829                 this.el.dom.value = Ext.value(this.lastSelectionText, '');
58830                 this.applyEmptyText();
58831             }else{
58832                 this.clearValue();
58833             }
58834         }else{
58835             if(rec){
58836                 val = rec.get(this.valueField || this.displayField);
58837             }
58838             this.setValue(val);
58839         }
58840
58841     },
58842
58843     // private
58844     onSelect : function(record, index){
58845         if(this.fireEvent('beforeselect', this, record, index) !== false){
58846             this.setValue(record.data[this.valueField || this.displayField]);
58847             this.collapse();
58848             this.fireEvent('select', this, record, index);
58849         }
58850     },
58851
58852     // inherit docs
58853     getName: function(){
58854         var hf = this.hiddenField;
58855         return hf && hf.name ? hf.name : this.hiddenName || Ext.form.ComboBox.superclass.getName.call(this);
58856     },
58857
58858     /**
58859      * Returns the currently selected field value or empty string if no value is set.
58860      * @return {String} value The selected value
58861      */
58862     getValue : function(){
58863         if(this.valueField){
58864             return Ext.isDefined(this.value) ? this.value : '';
58865         }else{
58866             return Ext.form.ComboBox.superclass.getValue.call(this);
58867         }
58868     },
58869
58870     /**
58871      * Clears any text/value currently set in the field
58872      */
58873     clearValue : function(){
58874         if(this.hiddenField){
58875             this.hiddenField.value = '';
58876         }
58877         this.setRawValue('');
58878         this.lastSelectionText = '';
58879         this.applyEmptyText();
58880         this.value = '';
58881     },
58882
58883     /**
58884      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
58885      * will be displayed in the field.  If the value does not match the data value of an existing item,
58886      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
58887      * Otherwise the field will be blank (although the value will still be set).
58888      * @param {String} value The value to match
58889      * @return {Ext.form.Field} this
58890      */
58891     setValue : function(v){
58892         var text = v;
58893         if(this.valueField){
58894             var r = this.findRecord(this.valueField, v);
58895             if(r){
58896                 text = r.data[this.displayField];
58897             }else if(Ext.isDefined(this.valueNotFoundText)){
58898                 text = this.valueNotFoundText;
58899             }
58900         }
58901         this.lastSelectionText = text;
58902         if(this.hiddenField){
58903             this.hiddenField.value = Ext.value(v, '');
58904         }
58905         Ext.form.ComboBox.superclass.setValue.call(this, text);
58906         this.value = v;
58907         return this;
58908     },
58909
58910     // private
58911     findRecord : function(prop, value){
58912         var record;
58913         if(this.store.getCount() > 0){
58914             this.store.each(function(r){
58915                 if(r.data[prop] == value){
58916                     record = r;
58917                     return false;
58918                 }
58919             });
58920         }
58921         return record;
58922     },
58923
58924     // private
58925     onViewMove : function(e, t){
58926         this.inKeyMode = false;
58927     },
58928
58929     // private
58930     onViewOver : function(e, t){
58931         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
58932             return;
58933         }
58934         var item = this.view.findItemFromChild(t);
58935         if(item){
58936             var index = this.view.indexOf(item);
58937             this.select(index, false);
58938         }
58939     },
58940
58941     // private
58942     onViewClick : function(doFocus){
58943         var index = this.view.getSelectedIndexes()[0],
58944             s = this.store,
58945             r = s.getAt(index);
58946         if(r){
58947             this.onSelect(r, index);
58948         }else {
58949             this.collapse();
58950         }
58951         if(doFocus !== false){
58952             this.el.focus();
58953         }
58954     },
58955
58956
58957     // private
58958     restrictHeight : function(){
58959         this.innerList.dom.style.height = '';
58960         var inner = this.innerList.dom,
58961             pad = this.list.getFrameWidth('tb') + (this.resizable ? this.handleHeight : 0) + this.assetHeight,
58962             h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight),
58963             ha = this.getPosition()[1]-Ext.getBody().getScroll().top,
58964             hb = Ext.lib.Dom.getViewHeight()-ha-this.getSize().height,
58965             space = Math.max(ha, hb, this.minHeight || 0)-this.list.shadowOffset-pad-5;
58966
58967         h = Math.min(h, space, this.maxHeight);
58968
58969         this.innerList.setHeight(h);
58970         this.list.beginUpdate();
58971         this.list.setHeight(h+pad);
58972         this.list.alignTo.apply(this.list, [this.el].concat(this.listAlign));
58973         this.list.endUpdate();
58974     },
58975
58976     /**
58977      * Returns true if the dropdown list is expanded, else false.
58978      */
58979     isExpanded : function(){
58980         return this.list && this.list.isVisible();
58981     },
58982
58983     /**
58984      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
58985      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
58986      * @param {String} value The data value of the item to select
58987      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
58988      * selected item if it is not currently in view (defaults to true)
58989      * @return {Boolean} True if the value matched an item in the list, else false
58990      */
58991     selectByValue : function(v, scrollIntoView){
58992         if(!Ext.isEmpty(v, true)){
58993             var r = this.findRecord(this.valueField || this.displayField, v);
58994             if(r){
58995                 this.select(this.store.indexOf(r), scrollIntoView);
58996                 return true;
58997             }
58998         }
58999         return false;
59000     },
59001
59002     /**
59003      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
59004      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
59005      * @param {Number} index The zero-based index of the list item to select
59006      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
59007      * selected item if it is not currently in view (defaults to true)
59008      */
59009     select : function(index, scrollIntoView){
59010         this.selectedIndex = index;
59011         this.view.select(index);
59012         if(scrollIntoView !== false){
59013             var el = this.view.getNode(index);
59014             if(el){
59015                 this.innerList.scrollChildIntoView(el, false);
59016             }
59017         }
59018
59019     },
59020
59021     // private
59022     selectNext : function(){
59023         var ct = this.store.getCount();
59024         if(ct > 0){
59025             if(this.selectedIndex == -1){
59026                 this.select(0);
59027             }else if(this.selectedIndex < ct-1){
59028                 this.select(this.selectedIndex+1);
59029             }
59030         }
59031     },
59032
59033     // private
59034     selectPrev : function(){
59035         var ct = this.store.getCount();
59036         if(ct > 0){
59037             if(this.selectedIndex == -1){
59038                 this.select(0);
59039             }else if(this.selectedIndex !== 0){
59040                 this.select(this.selectedIndex-1);
59041             }
59042         }
59043     },
59044
59045     // private
59046     onKeyUp : function(e){
59047         var k = e.getKey();
59048         if(this.editable !== false && this.readOnly !== true && (k == e.BACKSPACE || !e.isSpecialKey())){
59049
59050             this.lastKey = k;
59051             this.dqTask.delay(this.queryDelay);
59052         }
59053         Ext.form.ComboBox.superclass.onKeyUp.call(this, e);
59054     },
59055
59056     // private
59057     validateBlur : function(){
59058         return !this.list || !this.list.isVisible();
59059     },
59060
59061     // private
59062     initQuery : function(){
59063         this.doQuery(this.getRawValue());
59064     },
59065
59066     // private
59067     beforeBlur : function(){
59068         this.assertValue();
59069     },
59070
59071     // private
59072     postBlur  : function(){
59073         Ext.form.ComboBox.superclass.postBlur.call(this);
59074         this.collapse();
59075         this.inKeyMode = false;
59076     },
59077
59078     /**
59079      * Execute a query to filter the dropdown list.  Fires the {@link #beforequery} event prior to performing the
59080      * query allowing the query action to be canceled if needed.
59081      * @param {String} query The SQL query to execute
59082      * @param {Boolean} forceAll <tt>true</tt> to force the query to execute even if there are currently fewer
59083      * characters in the field than the minimum specified by the <tt>{@link #minChars}</tt> config option.  It
59084      * also clears any filter previously saved in the current store (defaults to <tt>false</tt>)
59085      */
59086     doQuery : function(q, forceAll){
59087         q = Ext.isEmpty(q) ? '' : q;
59088         var qe = {
59089             query: q,
59090             forceAll: forceAll,
59091             combo: this,
59092             cancel:false
59093         };
59094         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
59095             return false;
59096         }
59097         q = qe.query;
59098         forceAll = qe.forceAll;
59099         if(forceAll === true || (q.length >= this.minChars)){
59100             if(this.lastQuery !== q){
59101                 this.lastQuery = q;
59102                 if(this.mode == 'local'){
59103                     this.selectedIndex = -1;
59104                     if(forceAll){
59105                         this.store.clearFilter();
59106                     }else{
59107                         this.store.filter(this.displayField, q);
59108                     }
59109                     this.onLoad();
59110                 }else{
59111                     this.store.baseParams[this.queryParam] = q;
59112                     this.store.load({
59113                         params: this.getParams(q)
59114                     });
59115                     this.expand();
59116                 }
59117             }else{
59118                 this.selectedIndex = -1;
59119                 this.onLoad();
59120             }
59121         }
59122     },
59123
59124     // private
59125     getParams : function(q){
59126         var p = {};
59127         //p[this.queryParam] = q;
59128         if(this.pageSize){
59129             p.start = 0;
59130             p.limit = this.pageSize;
59131         }
59132         return p;
59133     },
59134
59135     /**
59136      * Hides the dropdown list if it is currently expanded. Fires the {@link #collapse} event on completion.
59137      */
59138     collapse : function(){
59139         if(!this.isExpanded()){
59140             return;
59141         }
59142         this.list.hide();
59143         Ext.getDoc().un('mousewheel', this.collapseIf, this);
59144         Ext.getDoc().un('mousedown', this.collapseIf, this);
59145         this.fireEvent('collapse', this);
59146     },
59147
59148     // private
59149     collapseIf : function(e){
59150         if(!e.within(this.wrap) && !e.within(this.list)){
59151             this.collapse();
59152         }
59153     },
59154
59155     /**
59156      * Expands the dropdown list if it is currently hidden. Fires the {@link #expand} event on completion.
59157      */
59158     expand : function(){
59159         if(this.isExpanded() || !this.hasFocus){
59160             return;
59161         }
59162         if(this.bufferSize){
59163             this.doResize(this.bufferSize);
59164             delete this.bufferSize;
59165         }
59166         this.list.alignTo.apply(this.list, [this.el].concat(this.listAlign));
59167         this.list.show();
59168         if(Ext.isGecko2){
59169             this.innerList.setOverflow('auto'); // necessary for FF 2.0/Mac
59170         }
59171         this.mon(Ext.getDoc(), {
59172             scope: this,
59173             mousewheel: this.collapseIf,
59174             mousedown: this.collapseIf
59175         });
59176         this.fireEvent('expand', this);
59177     },
59178
59179     /**
59180      * @method onTriggerClick
59181      * @hide
59182      */
59183     // private
59184     // Implements the default empty TriggerField.onTriggerClick function
59185     onTriggerClick : function(){
59186         if(this.readOnly || this.disabled){
59187             return;
59188         }
59189         if(this.isExpanded()){
59190             this.collapse();
59191             this.el.focus();
59192         }else {
59193             this.onFocus({});
59194             if(this.triggerAction == 'all') {
59195                 this.doQuery(this.allQuery, true);
59196             } else {
59197                 this.doQuery(this.getRawValue());
59198             }
59199             this.el.focus();
59200         }
59201     }
59202
59203     /**
59204      * @hide
59205      * @method autoSize
59206      */
59207     /**
59208      * @cfg {Boolean} grow @hide
59209      */
59210     /**
59211      * @cfg {Number} growMin @hide
59212      */
59213     /**
59214      * @cfg {Number} growMax @hide
59215      */
59216
59217 });
59218 Ext.reg('combo', Ext.form.ComboBox);
59219 /**
59220  * @class Ext.form.Checkbox
59221  * @extends Ext.form.Field
59222  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
59223  * @constructor
59224  * Creates a new Checkbox
59225  * @param {Object} config Configuration options
59226  * @xtype checkbox
59227  */
59228 Ext.form.Checkbox = Ext.extend(Ext.form.Field,  {
59229     /**
59230      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
59231      */
59232     focusClass : undefined,
59233     /**
59234      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to 'x-form-field')
59235      */
59236     fieldClass : 'x-form-field',
59237     /**
59238      * @cfg {Boolean} checked <tt>true</tt> if the checkbox should render initially checked (defaults to <tt>false</tt>)
59239      */
59240     checked : false,
59241     /**
59242      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
59243      * {tag: 'input', type: 'checkbox', autocomplete: 'off'})
59244      */
59245     defaultAutoCreate : { tag: 'input', type: 'checkbox', autocomplete: 'off'},
59246     /**
59247      * @cfg {String} boxLabel The text that appears beside the checkbox
59248      */
59249     /**
59250      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
59251      */
59252     /**
59253      * @cfg {Function} handler A function called when the {@link #checked} value changes (can be used instead of 
59254      * handling the check event). The handler is passed the following parameters:
59255      * <div class="mdetail-params"><ul>
59256      * <li><b>checkbox</b> : Ext.form.Checkbox<div class="sub-desc">The Checkbox being toggled.</div></li>
59257      * <li><b>checked</b> : Boolean<div class="sub-desc">The new checked state of the checkbox.</div></li>
59258      * </ul></div>
59259      */
59260     /**
59261      * @cfg {Object} scope An object to use as the scope ('this' reference) of the {@link #handler} function
59262      * (defaults to this Checkbox).
59263      */
59264
59265     // private
59266     actionMode : 'wrap',
59267     
59268         // private
59269     initComponent : function(){
59270         Ext.form.Checkbox.superclass.initComponent.call(this);
59271         this.addEvents(
59272             /**
59273              * @event check
59274              * Fires when the checkbox is checked or unchecked.
59275              * @param {Ext.form.Checkbox} this This checkbox
59276              * @param {Boolean} checked The new checked value
59277              */
59278             'check'
59279         );
59280     },
59281
59282     // private
59283     onResize : function(){
59284         Ext.form.Checkbox.superclass.onResize.apply(this, arguments);
59285         if(!this.boxLabel && !this.fieldLabel){
59286             this.el.alignTo(this.wrap, 'c-c');
59287         }
59288     },
59289
59290     // private
59291     initEvents : function(){
59292         Ext.form.Checkbox.superclass.initEvents.call(this);
59293         this.mon(this.el, {
59294             scope: this,
59295             click: this.onClick,
59296             change: this.onClick
59297         });
59298     },
59299
59300     /**
59301      * @hide
59302      * Overridden and disabled. The editor element does not support standard valid/invalid marking.
59303      * @method
59304      */
59305     markInvalid : Ext.emptyFn,
59306     /**
59307      * @hide
59308      * Overridden and disabled. The editor element does not support standard valid/invalid marking.
59309      * @method
59310      */
59311     clearInvalid : Ext.emptyFn,
59312
59313     // private
59314     onRender : function(ct, position){
59315         Ext.form.Checkbox.superclass.onRender.call(this, ct, position);
59316         if(this.inputValue !== undefined){
59317             this.el.dom.value = this.inputValue;
59318         }
59319         this.wrap = this.el.wrap({cls: 'x-form-check-wrap'});
59320         if(this.boxLabel){
59321             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
59322         }
59323         if(this.checked){
59324             this.setValue(true);
59325         }else{
59326             this.checked = this.el.dom.checked;
59327         }
59328         // Need to repaint for IE, otherwise positioning is broken
59329         if(Ext.isIE){
59330             this.wrap.repaint();
59331         }
59332         this.resizeEl = this.positionEl = this.wrap;
59333     },
59334
59335     // private
59336     onDestroy : function(){
59337         Ext.destroy(this.wrap);
59338         Ext.form.Checkbox.superclass.onDestroy.call(this);
59339     },
59340
59341     // private
59342     initValue : function() {
59343         this.originalValue = this.getValue();
59344     },
59345
59346     /**
59347      * Returns the checked state of the checkbox.
59348      * @return {Boolean} True if checked, else false
59349      */
59350     getValue : function(){
59351         if(this.rendered){
59352             return this.el.dom.checked;
59353         }
59354         return this.checked;
59355     },
59356
59357         // private
59358     onClick : function(){
59359         if(this.el.dom.checked != this.checked){
59360             this.setValue(this.el.dom.checked);
59361         }
59362     },
59363
59364     /**
59365      * Sets the checked state of the checkbox, fires the 'check' event, and calls a
59366      * <code>{@link #handler}</code> (if configured).
59367      * @param {Boolean/String} checked The following values will check the checkbox:
59368      * <code>true, 'true', '1', or 'on'</code>. Any other value will uncheck the checkbox.
59369      * @return {Ext.form.Field} this
59370      */
59371     setValue : function(v){
59372         var checked = this.checked ;
59373         this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
59374         if(this.rendered){
59375             this.el.dom.checked = this.checked;
59376             this.el.dom.defaultChecked = this.checked;
59377         }
59378         if(checked != this.checked){
59379             this.fireEvent('check', this, this.checked);
59380             if(this.handler){
59381                 this.handler.call(this.scope || this, this, this.checked);
59382             }
59383         }
59384         return this;
59385     }
59386 });
59387 Ext.reg('checkbox', Ext.form.Checkbox);
59388 /**
59389  * @class Ext.form.CheckboxGroup
59390  * @extends Ext.form.Field
59391  * <p>A grouping container for {@link Ext.form.Checkbox} controls.</p>
59392  * <p>Sample usage:</p>
59393  * <pre><code>
59394 var myCheckboxGroup = new Ext.form.CheckboxGroup({
59395     id:'myGroup',
59396     xtype: 'checkboxgroup',
59397     fieldLabel: 'Single Column',
59398     itemCls: 'x-check-group-alt',
59399     // Put all controls in a single column with width 100%
59400     columns: 1,
59401     items: [
59402         {boxLabel: 'Item 1', name: 'cb-col-1'},
59403         {boxLabel: 'Item 2', name: 'cb-col-2', checked: true},
59404         {boxLabel: 'Item 3', name: 'cb-col-3'}
59405     ]
59406 });
59407  * </code></pre>
59408  * @constructor
59409  * Creates a new CheckboxGroup
59410  * @param {Object} config Configuration options
59411  * @xtype checkboxgroup
59412  */
59413 Ext.form.CheckboxGroup = Ext.extend(Ext.form.Field, {
59414     /**
59415      * @cfg {Array} items An Array of {@link Ext.form.Checkbox Checkbox}es or Checkbox config objects
59416      * to arrange in the group.
59417      */
59418     /**
59419      * @cfg {String/Number/Array} columns Specifies the number of columns to use when displaying grouped
59420      * checkbox/radio controls using automatic layout.  This config can take several types of values:
59421      * <ul><li><b>'auto'</b> : <p class="sub-desc">The controls will be rendered one per column on one row and the width
59422      * of each column will be evenly distributed based on the width of the overall field container. This is the default.</p></li>
59423      * <li><b>Number</b> : <p class="sub-desc">If you specific a number (e.g., 3) that number of columns will be
59424      * created and the contained controls will be automatically distributed based on the value of {@link #vertical}.</p></li>
59425      * <li><b>Array</b> : Object<p class="sub-desc">You can also specify an array of column widths, mixing integer
59426      * (fixed width) and float (percentage width) values as needed (e.g., [100, .25, .75]). Any integer values will
59427      * be rendered first, then any float values will be calculated as a percentage of the remaining space. Float
59428      * values do not have to add up to 1 (100%) although if you want the controls to take up the entire field
59429      * container you should do so.</p></li></ul>
59430      */
59431     columns : 'auto',
59432     /**
59433      * @cfg {Boolean} vertical True to distribute contained controls across columns, completely filling each column
59434      * top to bottom before starting on the next column.  The number of controls in each column will be automatically
59435      * calculated to keep columns as even as possible.  The default value is false, so that controls will be added
59436      * to columns one at a time, completely filling each row left to right before starting on the next row.
59437      */
59438     vertical : false,
59439     /**
59440      * @cfg {Boolean} allowBlank False to validate that at least one item in the group is checked (defaults to true).
59441      * If no items are selected at validation time, {@link @blankText} will be used as the error text.
59442      */
59443     allowBlank : true,
59444     /**
59445      * @cfg {String} blankText Error text to display if the {@link #allowBlank} validation fails (defaults to "You must
59446      * select at least one item in this group")
59447      */
59448     blankText : "You must select at least one item in this group",
59449
59450     // private
59451     defaultType : 'checkbox',
59452
59453     // private
59454     groupCls : 'x-form-check-group',
59455
59456     // private
59457     initComponent: function(){
59458         this.addEvents(
59459             /**
59460              * @event change
59461              * Fires when the state of a child checkbox changes.
59462              * @param {Ext.form.CheckboxGroup} this
59463              * @param {Array} checked An array containing the checked boxes.
59464              */
59465             'change'
59466         );
59467         this.on('change', this.validate, this);
59468         Ext.form.CheckboxGroup.superclass.initComponent.call(this);
59469     },
59470
59471     // private
59472     onRender : function(ct, position){
59473         if(!this.el){
59474             var panelCfg = {
59475                 autoEl: {
59476                     id: this.id
59477                 },
59478                 cls: this.groupCls,
59479                 layout: 'column',
59480                 renderTo: ct,
59481                 bufferResize: false // Default this to false, since it doesn't really have a proper ownerCt.
59482             };
59483             var colCfg = {
59484                 xtype: 'container',
59485                 defaultType: this.defaultType,
59486                 layout: 'form',
59487                 defaults: {
59488                     hideLabel: true,
59489                     anchor: '100%'
59490                 }
59491             };
59492
59493             if(this.items[0].items){
59494
59495                 // The container has standard ColumnLayout configs, so pass them in directly
59496
59497                 Ext.apply(panelCfg, {
59498                     layoutConfig: {columns: this.items.length},
59499                     defaults: this.defaults,
59500                     items: this.items
59501                 });
59502                 for(var i=0, len=this.items.length; i<len; i++){
59503                     Ext.applyIf(this.items[i], colCfg);
59504                 }
59505
59506             }else{
59507
59508                 // The container has field item configs, so we have to generate the column
59509                 // panels first then move the items into the columns as needed.
59510
59511                 var numCols, cols = [];
59512
59513                 if(typeof this.columns == 'string'){ // 'auto' so create a col per item
59514                     this.columns = this.items.length;
59515                 }
59516                 if(!Ext.isArray(this.columns)){
59517                     var cs = [];
59518                     for(var i=0; i<this.columns; i++){
59519                         cs.push((100/this.columns)*.01); // distribute by even %
59520                     }
59521                     this.columns = cs;
59522                 }
59523
59524                 numCols = this.columns.length;
59525
59526                 // Generate the column configs with the correct width setting
59527                 for(var i=0; i<numCols; i++){
59528                     var cc = Ext.apply({items:[]}, colCfg);
59529                     cc[this.columns[i] <= 1 ? 'columnWidth' : 'width'] = this.columns[i];
59530                     if(this.defaults){
59531                         cc.defaults = Ext.apply(cc.defaults || {}, this.defaults)
59532                     }
59533                     cols.push(cc);
59534                 };
59535
59536                 // Distribute the original items into the columns
59537                 if(this.vertical){
59538                     var rows = Math.ceil(this.items.length / numCols), ri = 0;
59539                     for(var i=0, len=this.items.length; i<len; i++){
59540                         if(i>0 && i%rows==0){
59541                             ri++;
59542                         }
59543                         if(this.items[i].fieldLabel){
59544                             this.items[i].hideLabel = false;
59545                         }
59546                         cols[ri].items.push(this.items[i]);
59547                     };
59548                 }else{
59549                     for(var i=0, len=this.items.length; i<len; i++){
59550                         var ci = i % numCols;
59551                         if(this.items[i].fieldLabel){
59552                             this.items[i].hideLabel = false;
59553                         }
59554                         cols[ci].items.push(this.items[i]);
59555                     };
59556                 }
59557
59558                 Ext.apply(panelCfg, {
59559                     layoutConfig: {columns: numCols},
59560                     items: cols
59561                 });
59562             }
59563
59564             this.panel = new Ext.Container(panelCfg);
59565             this.panel.ownerCt = this;
59566             this.el = this.panel.getEl();
59567
59568             if(this.forId && this.itemCls){
59569                 var l = this.el.up(this.itemCls).child('label', true);
59570                 if(l){
59571                     l.setAttribute('htmlFor', this.forId);
59572                 }
59573             }
59574
59575             var fields = this.panel.findBy(function(c){
59576                 return c.isFormField;
59577             }, this);
59578
59579             this.items = new Ext.util.MixedCollection();
59580             this.items.addAll(fields);
59581         }
59582         Ext.form.CheckboxGroup.superclass.onRender.call(this, ct, position);
59583     },
59584
59585     initValue : function(){
59586         if(this.value){
59587             this.setValue.apply(this, this.buffered ? this.value : [this.value]);
59588             delete this.buffered;
59589             delete this.value;
59590         }
59591     },
59592
59593     afterRender : function(){
59594         Ext.form.CheckboxGroup.superclass.afterRender.call(this);
59595         this.eachItem(function(item){
59596             item.on('check', this.fireChecked, this);
59597             item.inGroup = true;
59598         });
59599     },
59600
59601     // private
59602     doLayout: function(){
59603         //ugly method required to layout hidden items
59604         if(this.rendered){
59605             this.panel.forceLayout = this.ownerCt.forceLayout;
59606             this.panel.doLayout();
59607         }
59608     },
59609
59610     // private
59611     fireChecked: function(){
59612         var arr = [];
59613         this.eachItem(function(item){
59614             if(item.checked){
59615                 arr.push(item);
59616             }
59617         });
59618         this.fireEvent('change', this, arr);
59619     },
59620
59621     // private
59622     validateValue : function(value){
59623         if(!this.allowBlank){
59624             var blank = true;
59625             this.eachItem(function(f){
59626                 if(f.checked){
59627                     return (blank = false);
59628                 }
59629             });
59630             if(blank){
59631                 this.markInvalid(this.blankText);
59632                 return false;
59633             }
59634         }
59635         return true;
59636     },
59637
59638     // private
59639     isDirty: function(){
59640         //override the behaviour to check sub items.
59641         if (this.disabled || !this.rendered) {
59642             return false;
59643         }
59644
59645         var dirty = false;
59646         this.eachItem(function(item){
59647             if(item.isDirty()){
59648                 dirty = true;
59649                 return false;
59650             }
59651         });
59652         return dirty;
59653     },
59654
59655     // private
59656     onDisable : function(){
59657         this.eachItem(function(item){
59658             item.disable();
59659         });
59660     },
59661
59662     // private
59663     onEnable : function(){
59664         this.eachItem(function(item){
59665             item.enable();
59666         });
59667     },
59668
59669     // private
59670     doLayout: function(){
59671         if(this.rendered){
59672             this.panel.forceLayout = this.ownerCt.forceLayout;
59673             this.panel.doLayout();
59674         }
59675     },
59676
59677     // private
59678     onResize : function(w, h){
59679         this.panel.setSize(w, h);
59680         this.panel.doLayout();
59681     },
59682
59683     // inherit docs from Field
59684     reset : function(){
59685         this.eachItem(function(c){
59686             if(c.reset){
59687                 c.reset();
59688             }
59689         });
59690         // Defer the clearInvalid so if BaseForm's collection is being iterated it will be called AFTER it is complete.
59691         // Important because reset is being called on both the group and the individual items.
59692         (function() {
59693             this.clearInvalid();
59694         }).defer(50, this);
59695     },
59696
59697     /**
59698      * {@link Ext.form.Checkbox#setValue Set the value(s)} of an item or items
59699      * in the group. Examples illustrating how this method may be called:
59700      * <pre><code>
59701 // call with name and value
59702 myCheckboxGroup.setValue('cb-col-1', true);
59703 // call with an array of boolean values
59704 myCheckboxGroup.setValue([true, false, false]);
59705 // call with an object literal specifying item:value pairs
59706 myCheckboxGroup.setValue({
59707     'cb-col-2': false,
59708     'cb-col-3': true
59709 });
59710 // use comma separated string to set items with name to true (checked)
59711 myCheckboxGroup.setValue('cb-col-1,cb-col-3');
59712      * </code></pre>
59713      * See {@link Ext.form.Checkbox#setValue} for additional information.
59714      * @param {Mixed} id The checkbox to check, or as described by example shown.
59715      * @param {Boolean} value (optional) The value to set the item.
59716      * @return {Ext.form.CheckboxGroup} this
59717      */
59718     setValue: function(){
59719         if(this.rendered){
59720             this.onSetValue.apply(this, arguments);
59721         }else{
59722             this.buffered = true;
59723             this.value = arguments;
59724         }
59725         return this;
59726     },
59727
59728     onSetValue: function(id, value){
59729         if(arguments.length == 1){
59730             if(Ext.isArray(id)){
59731                 // an array of boolean values
59732                 Ext.each(id, function(val, idx){
59733                     var item = this.items.itemAt(idx);
59734                     if(item){
59735                         item.setValue(val);
59736                     }
59737                 }, this);
59738             }else if(Ext.isObject(id)){
59739                 // set of name/value pairs
59740                 for(var i in id){
59741                     var f = this.getBox(i);
59742                     if(f){
59743                         f.setValue(id[i]);
59744                     }
59745                 }
59746             }else{
59747                 this.setValueForItem(id);
59748             }
59749         }else{
59750             var f = this.getBox(id);
59751             if(f){
59752                 f.setValue(value);
59753             }
59754         }
59755     },
59756
59757     // private
59758     beforeDestroy: function(){
59759         Ext.destroy(this.panel);
59760         Ext.form.CheckboxGroup.superclass.beforeDestroy.call(this);
59761
59762     },
59763
59764     setValueForItem : function(val){
59765         val = String(val).split(',');
59766         this.eachItem(function(item){
59767             if(val.indexOf(item.inputValue)> -1){
59768                 item.setValue(true);
59769             }
59770         });
59771     },
59772
59773     // private
59774     getBox : function(id){
59775         var box = null;
59776         this.eachItem(function(f){
59777             if(id == f || f.dataIndex == id || f.id == id || f.getName() == id){
59778                 box = f;
59779                 return false;
59780             }
59781         });
59782         return box;
59783     },
59784
59785     /**
59786      * Gets an array of the selected {@link Ext.form.Checkbox} in the group.
59787      * @return {Array} An array of the selected checkboxes.
59788      */
59789     getValue : function(){
59790         var out = [];
59791         this.eachItem(function(item){
59792             if(item.checked){
59793                 out.push(item);
59794             }
59795         });
59796         return out;
59797     },
59798
59799     // private
59800     eachItem: function(fn){
59801         if(this.items && this.items.each){
59802             this.items.each(fn, this);
59803         }
59804     },
59805
59806     /**
59807      * @cfg {String} name
59808      * @hide
59809      */
59810
59811     /**
59812      * @method getRawValue
59813      * @hide
59814      */
59815     getRawValue : Ext.emptyFn,
59816
59817     /**
59818      * @method setRawValue
59819      * @hide
59820      */
59821     setRawValue : Ext.emptyFn
59822
59823 });
59824
59825 Ext.reg('checkboxgroup', Ext.form.CheckboxGroup);
59826 /**
59827  * @class Ext.form.Radio
59828  * @extends Ext.form.Checkbox
59829  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
59830  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
59831  * @constructor
59832  * Creates a new Radio
59833  * @param {Object} config Configuration options
59834  * @xtype radio
59835  */
59836 Ext.form.Radio = Ext.extend(Ext.form.Checkbox, {
59837     inputType: 'radio',
59838
59839     /**
59840      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
59841      * @method
59842      */
59843     markInvalid : Ext.emptyFn,
59844     /**
59845      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
59846      * @method
59847      */
59848     clearInvalid : Ext.emptyFn,
59849
59850     /**
59851      * If this radio is part of a group, it will return the selected value
59852      * @return {String}
59853      */
59854     getGroupValue : function(){
59855         var p = this.el.up('form') || Ext.getBody();
59856         var c = p.child('input[name='+this.el.dom.name+']:checked', true);
59857         return c ? c.value : null;
59858     },
59859
59860     // private
59861     onClick : function(){
59862         if(this.el.dom.checked != this.checked){
59863                         var els = this.getCheckEl().select('input[name=' + this.el.dom.name + ']');
59864                         els.each(function(el){
59865                                 if(el.dom.id == this.id){
59866                                         this.setValue(true);
59867                                 }else{
59868                                         Ext.getCmp(el.dom.id).setValue(false);
59869                                 }
59870                         }, this);
59871                 }
59872     },
59873
59874     /**
59875      * Sets either the checked/unchecked status of this Radio, or, if a string value
59876      * is passed, checks a sibling Radio of the same name whose value is the value specified.
59877      * @param value {String/Boolean} Checked value, or the value of the sibling radio button to check.
59878      * @return {Ext.form.Field} this
59879      */
59880     setValue : function(v){
59881         if (typeof v == 'boolean') {
59882             Ext.form.Radio.superclass.setValue.call(this, v);
59883         } else if (this.rendered) {
59884             var r = this.getCheckEl().child('input[name=' + this.el.dom.name + '][value=' + v + ']', true);
59885             if(r){
59886                 Ext.getCmp(r.id).setValue(true);
59887             }
59888         }
59889         return this;
59890     },
59891
59892     // private
59893     getCheckEl: function(){
59894         if(this.inGroup){
59895             return this.el.up('.x-form-radio-group')
59896         }
59897         return this.el.up('form') || Ext.getBody();
59898     }
59899 });
59900 Ext.reg('radio', Ext.form.Radio);
59901 /**
59902  * @class Ext.form.RadioGroup
59903  * @extends Ext.form.CheckboxGroup
59904  * A grouping container for {@link Ext.form.Radio} controls.
59905  * @constructor
59906  * Creates a new RadioGroup
59907  * @param {Object} config Configuration options
59908  * @xtype radiogroup
59909  */
59910 Ext.form.RadioGroup = Ext.extend(Ext.form.CheckboxGroup, {
59911     /**
59912      * @cfg {Array} items An Array of {@link Ext.form.Radio Radio}s or Radio config objects
59913      * to arrange in the group.
59914      */
59915     /**
59916      * @cfg {Boolean} allowBlank True to allow every item in the group to be blank (defaults to true).
59917      * If allowBlank = false and no items are selected at validation time, {@link @blankText} will
59918      * be used as the error text.
59919      */
59920     allowBlank : true,
59921     /**
59922      * @cfg {String} blankText Error text to display if the {@link #allowBlank} validation fails
59923      * (defaults to 'You must select one item in this group')
59924      */
59925     blankText : 'You must select one item in this group',
59926     
59927     // private
59928     defaultType : 'radio',
59929     
59930     // private
59931     groupCls : 'x-form-radio-group',
59932     
59933     /**
59934      * @event change
59935      * Fires when the state of a child radio changes.
59936      * @param {Ext.form.RadioGroup} this
59937      * @param {Ext.form.Radio} checked The checked radio
59938      */
59939     
59940     /**
59941      * Gets the selected {@link Ext.form.Radio} in the group, if it exists.
59942      * @return {Ext.form.Radio} The selected radio.
59943      */
59944     getValue : function(){
59945         var out = null;
59946         this.eachItem(function(item){
59947             if(item.checked){
59948                 out = item;
59949                 return false;
59950             }
59951         });
59952         return out;
59953     },
59954     
59955     /**
59956      * Sets the checked radio in the group.
59957      * @param {String/Ext.form.Radio} id The radio to check.
59958      * @param {Boolean} value The value to set the radio.
59959      * @return {Ext.form.RadioGroup} this
59960      */
59961     onSetValue : function(id, value){
59962         if(arguments.length > 1){
59963             var f = this.getBox(id);
59964             if(f){
59965                 f.setValue(value);
59966                 if(f.checked){
59967                     this.eachItem(function(item){
59968                         if (item !== f){
59969                             item.setValue(false);
59970                         }
59971                     });
59972                 }
59973             }
59974         }else{
59975             this.setValueForItem(id);
59976         }
59977     },
59978     
59979     setValueForItem : function(val){
59980         val = String(val).split(',')[0];
59981         this.eachItem(function(item){
59982             item.setValue(val == item.inputValue);
59983         });
59984     },
59985     
59986     // private
59987     fireChecked : function(){
59988         if(!this.checkTask){
59989             this.checkTask = new Ext.util.DelayedTask(this.bufferChecked, this);
59990         }
59991         this.checkTask.delay(10);
59992     },
59993     
59994     // private
59995     bufferChecked : function(){
59996         var out = null;
59997         this.eachItem(function(item){
59998             if(item.checked){
59999                 out = item;
60000                 return false;
60001             }
60002         });
60003         this.fireEvent('change', this, out);
60004     },
60005     
60006     onDestroy : function(){
60007         if(this.checkTask){
60008             this.checkTask.cancel();
60009             this.checkTask = null;
60010         }
60011         Ext.form.RadioGroup.superclass.onDestroy.call(this);
60012     }
60013
60014 });
60015
60016 Ext.reg('radiogroup', Ext.form.RadioGroup);
60017 /**\r
60018  * @class Ext.form.Hidden\r
60019  * @extends Ext.form.Field\r
60020  * A basic hidden field for storing hidden values in forms that need to be passed in the form submit.\r
60021  * @constructor\r
60022  * Create a new Hidden field.\r
60023  * @param {Object} config Configuration options\r
60024  * @xtype hidden\r
60025  */\r
60026 Ext.form.Hidden = Ext.extend(Ext.form.Field, {\r
60027     // private\r
60028     inputType : 'hidden',\r
60029 \r
60030     // private\r
60031     onRender : function(){\r
60032         Ext.form.Hidden.superclass.onRender.apply(this, arguments);\r
60033     },\r
60034 \r
60035     // private\r
60036     initEvents : function(){\r
60037         this.originalValue = this.getValue();\r
60038     },\r
60039 \r
60040     // These are all private overrides\r
60041     setSize : Ext.emptyFn,\r
60042     setWidth : Ext.emptyFn,\r
60043     setHeight : Ext.emptyFn,\r
60044     setPosition : Ext.emptyFn,\r
60045     setPagePosition : Ext.emptyFn,\r
60046     markInvalid : Ext.emptyFn,\r
60047     clearInvalid : Ext.emptyFn\r
60048 });\r
60049 Ext.reg('hidden', Ext.form.Hidden);/**
60050  * @class Ext.form.BasicForm
60051  * @extends Ext.util.Observable
60052  * <p>Encapsulates the DOM &lt;form> element at the heart of the {@link Ext.form.FormPanel FormPanel} class, and provides
60053  * input field management, validation, submission, and form loading services.</p>
60054  * <p>By default, Ext Forms are submitted through Ajax, using an instance of {@link Ext.form.Action.Submit}.
60055  * To enable normal browser submission of an Ext Form, use the {@link #standardSubmit} config option.</p>
60056  * <p><b><u>File Uploads</u></b></p>
60057  * <p>{@link #fileUpload File uploads} are not performed using Ajax submission, that
60058  * is they are <b>not</b> performed using XMLHttpRequests. Instead the form is submitted in the standard
60059  * manner with the DOM <tt>&lt;form></tt> element temporarily modified to have its
60060  * <a href="http://www.w3.org/TR/REC-html40/present/frames.html#adef-target">target</a> set to refer
60061  * to a dynamically generated, hidden <tt>&lt;iframe></tt> which is inserted into the document
60062  * but removed after the return data has been gathered.</p>
60063  * <p>The server response is parsed by the browser to create the document for the IFRAME. If the
60064  * server is using JSON to send the return object, then the
60065  * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.17">Content-Type</a> header
60066  * must be set to "text/html" in order to tell the browser to insert the text unchanged into the document body.</p>
60067  * <p>Characters which are significant to an HTML parser must be sent as HTML entities, so encode
60068  * "&lt;" as "&amp;lt;", "&amp;" as "&amp;amp;" etc.</p>
60069  * <p>The response text is retrieved from the document, and a fake XMLHttpRequest object
60070  * is created containing a <tt>responseText</tt> property in order to conform to the
60071  * requirements of event handlers and callbacks.</p>
60072  * <p>Be aware that file upload packets are sent with the content type <a href="http://www.faqs.org/rfcs/rfc2388.html">multipart/form</a>
60073  * and some server technologies (notably JEE) may require some custom processing in order to
60074  * retrieve parameter names and parameter values from the packet content.</p>
60075  * @constructor
60076  * @param {Mixed} el The form element or its id
60077  * @param {Object} config Configuration options
60078  */
60079 Ext.form.BasicForm = Ext.extend(Ext.util.Observable, {
60080     
60081     constructor: function(el, config){
60082         Ext.apply(this, config);
60083         if(Ext.isString(this.paramOrder)){
60084             this.paramOrder = this.paramOrder.split(/[\s,|]/);
60085         }
60086         /**
60087          * A {@link Ext.util.MixedCollection MixedCollection} containing all the Ext.form.Fields in this form.
60088          * @type MixedCollection
60089          * @property items
60090          */
60091         this.items = new Ext.util.MixedCollection(false, function(o){
60092             return o.getItemId();
60093         });
60094         this.addEvents(
60095             /**
60096              * @event beforeaction
60097              * Fires before any action is performed. Return false to cancel the action.
60098              * @param {Form} this
60099              * @param {Action} action The {@link Ext.form.Action} to be performed
60100              */
60101             'beforeaction',
60102             /**
60103              * @event actionfailed
60104              * Fires when an action fails.
60105              * @param {Form} this
60106              * @param {Action} action The {@link Ext.form.Action} that failed
60107              */
60108             'actionfailed',
60109             /**
60110              * @event actioncomplete
60111              * Fires when an action is completed.
60112              * @param {Form} this
60113              * @param {Action} action The {@link Ext.form.Action} that completed
60114              */
60115             'actioncomplete'
60116         );
60117
60118         if(el){
60119             this.initEl(el);
60120         }
60121         Ext.form.BasicForm.superclass.constructor.call(this);    
60122     },
60123     
60124     /**
60125      * @cfg {String} method
60126      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
60127      */
60128     /**
60129      * @cfg {DataReader} reader
60130      * An Ext.data.DataReader (e.g. {@link Ext.data.XmlReader}) to be used to read
60131      * data when executing 'load' actions. This is optional as there is built-in
60132      * support for processing JSON.  For additional information on using an XMLReader
60133      * see the example provided in examples/form/xml-form.html.
60134      */
60135     /**
60136      * @cfg {DataReader} errorReader
60137      * <p>An Ext.data.DataReader (e.g. {@link Ext.data.XmlReader}) to be used to
60138      * read field error messages returned from 'submit' actions. This is optional
60139      * as there is built-in support for processing JSON.</p>
60140      * <p>The Records which provide messages for the invalid Fields must use the
60141      * Field name (or id) as the Record ID, and must contain a field called 'msg'
60142      * which contains the error message.</p>
60143      * <p>The errorReader does not have to be a full-blown implementation of a
60144      * DataReader. It simply needs to implement a <tt>read(xhr)</tt> function
60145      * which returns an Array of Records in an object with the following
60146      * structure:</p><pre><code>
60147 {
60148     records: recordArray
60149 }
60150 </code></pre>
60151      */
60152     /**
60153      * @cfg {String} url
60154      * The URL to use for form actions if one isn't supplied in the
60155      * <code>{@link #doAction doAction} options</code>.
60156      */
60157     /**
60158      * @cfg {Boolean} fileUpload
60159      * Set to true if this form is a file upload.
60160      * <p>File uploads are not performed using normal 'Ajax' techniques, that is they are <b>not</b>
60161      * performed using XMLHttpRequests. Instead the form is submitted in the standard manner with the
60162      * DOM <tt>&lt;form></tt> element temporarily modified to have its
60163      * <a href="http://www.w3.org/TR/REC-html40/present/frames.html#adef-target">target</a> set to refer
60164      * to a dynamically generated, hidden <tt>&lt;iframe></tt> which is inserted into the document
60165      * but removed after the return data has been gathered.</p>
60166      * <p>The server response is parsed by the browser to create the document for the IFRAME. If the
60167      * server is using JSON to send the return object, then the
60168      * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.17">Content-Type</a> header
60169      * must be set to "text/html" in order to tell the browser to insert the text unchanged into the document body.</p>
60170      * <p>Characters which are significant to an HTML parser must be sent as HTML entities, so encode
60171      * "&lt;" as "&amp;lt;", "&amp;" as "&amp;amp;" etc.</p>
60172      * <p>The response text is retrieved from the document, and a fake XMLHttpRequest object
60173      * is created containing a <tt>responseText</tt> property in order to conform to the
60174      * requirements of event handlers and callbacks.</p>
60175      * <p>Be aware that file upload packets are sent with the content type <a href="http://www.faqs.org/rfcs/rfc2388.html">multipart/form</a>
60176      * and some server technologies (notably JEE) may require some custom processing in order to
60177      * retrieve parameter names and parameter values from the packet content.</p>
60178      */
60179     /**
60180      * @cfg {Object} baseParams
60181      * <p>Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.</p>
60182      * <p>Parameters are encoded as standard HTTP parameters using {@link Ext#urlEncode}.</p>
60183      */
60184     /**
60185      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
60186      */
60187     timeout: 30,
60188
60189     /**
60190      * @cfg {Object} api (Optional) If specified load and submit actions will be handled
60191      * with {@link Ext.form.Action.DirectLoad} and {@link Ext.form.Action.DirectSubmit}.
60192      * Methods which have been imported by Ext.Direct can be specified here to load and submit
60193      * forms.
60194      * Such as the following:<pre><code>
60195 api: {
60196     load: App.ss.MyProfile.load,
60197     submit: App.ss.MyProfile.submit
60198 }
60199 </code></pre>
60200      * <p>Load actions can use <code>{@link #paramOrder}</code> or <code>{@link #paramsAsHash}</code>
60201      * to customize how the load method is invoked.
60202      * Submit actions will always use a standard form submit. The formHandler configuration must
60203      * be set on the associated server-side method which has been imported by Ext.Direct</p>
60204      */
60205
60206     /**
60207      * @cfg {Array/String} paramOrder <p>A list of params to be executed server side.
60208      * Defaults to <tt>undefined</tt>. Only used for the <code>{@link #api}</code>
60209      * <code>load</code> configuration.</p>
60210      * <br><p>Specify the params in the order in which they must be executed on the
60211      * server-side as either (1) an Array of String values, or (2) a String of params
60212      * delimited by either whitespace, comma, or pipe. For example,
60213      * any of the following would be acceptable:</p><pre><code>
60214 paramOrder: ['param1','param2','param3']
60215 paramOrder: 'param1 param2 param3'
60216 paramOrder: 'param1,param2,param3'
60217 paramOrder: 'param1|param2|param'
60218      </code></pre>
60219      */
60220     paramOrder: undefined,
60221
60222     /**
60223      * @cfg {Boolean} paramsAsHash Only used for the <code>{@link #api}</code>
60224      * <code>load</code> configuration. Send parameters as a collection of named
60225      * arguments (defaults to <tt>false</tt>). Providing a
60226      * <tt>{@link #paramOrder}</tt> nullifies this configuration.
60227      */
60228     paramsAsHash: false,
60229     
60230     /**
60231      * @cfg {String} waitTitle
60232      * The default title to show for the waiting message box (defaults to <tt>'Please Wait...'</tt>)
60233      */
60234     waitTitle: 'Please Wait...',
60235
60236     // private
60237     activeAction : null,
60238
60239     /**
60240      * @cfg {Boolean} trackResetOnLoad If set to <tt>true</tt>, {@link #reset}() resets to the last loaded
60241      * or {@link #setValues}() data instead of when the form was first created.  Defaults to <tt>false</tt>.
60242      */
60243     trackResetOnLoad : false,
60244
60245     /**
60246      * @cfg {Boolean} standardSubmit
60247      * <p>If set to <tt>true</tt>, standard HTML form submits are used instead
60248      * of XHR (Ajax) style form submissions. Defaults to <tt>false</tt>.</p>
60249      * <br><p><b>Note:</b> When using <code>standardSubmit</code>, the
60250      * <code>options</code> to <code>{@link #submit}</code> are ignored because
60251      * Ext's Ajax infrastracture is bypassed. To pass extra parameters (e.g.
60252      * <code>baseParams</code> and <code>params</code>), utilize hidden fields
60253      * to submit extra data, for example:</p>
60254      * <pre><code>
60255 new Ext.FormPanel({
60256     standardSubmit: true,
60257     baseParams: {
60258         foo: 'bar'
60259     },
60260     {@link url}: 'myProcess.php',
60261     items: [{
60262         xtype: 'textfield',
60263         name: 'userName'
60264     }],
60265     buttons: [{
60266         text: 'Save',
60267         handler: function(){
60268             var fp = this.ownerCt.ownerCt,
60269                 form = fp.getForm();
60270             if (form.isValid()) {
60271                 // check if there are baseParams and if
60272                 // hiddent items have been added already
60273                 if (fp.baseParams && !fp.paramsAdded) {
60274                     // add hidden items for all baseParams
60275                     for (i in fp.baseParams) {
60276                         fp.add({
60277                             xtype: 'hidden',
60278                             name: i,
60279                             value: fp.baseParams[i]
60280                         });
60281                     }
60282                     fp.doLayout();
60283                     // set a custom flag to prevent re-adding
60284                     fp.paramsAdded = true;
60285                 }
60286                 form.{@link #submit}();
60287             }
60288         }
60289     }]
60290 });
60291      * </code></pre>
60292      */
60293     /**
60294      * By default wait messages are displayed with Ext.MessageBox.wait. You can target a specific
60295      * element by passing it or its id or mask the form itself by passing in true.
60296      * @type Mixed
60297      * @property waitMsgTarget
60298      */
60299
60300     // private
60301     initEl : function(el){
60302         this.el = Ext.get(el);
60303         this.id = this.el.id || Ext.id();
60304         if(!this.standardSubmit){
60305             this.el.on('submit', this.onSubmit, this);
60306         }
60307         this.el.addClass('x-form');
60308     },
60309
60310     /**
60311      * Get the HTML form Element
60312      * @return Ext.Element
60313      */
60314     getEl: function(){
60315         return this.el;
60316     },
60317
60318     // private
60319     onSubmit : function(e){
60320         e.stopEvent();
60321     },
60322
60323     // private
60324     destroy: function() {
60325         this.items.each(function(f){
60326             Ext.destroy(f);
60327         });
60328         if(this.el){
60329             this.el.removeAllListeners();
60330             this.el.remove();
60331         }
60332         this.purgeListeners();
60333     },
60334
60335     /**
60336      * Returns true if client-side validation on the form is successful.
60337      * @return Boolean
60338      */
60339     isValid : function(){
60340         var valid = true;
60341         this.items.each(function(f){
60342            if(!f.validate()){
60343                valid = false;
60344            }
60345         });
60346         return valid;
60347     },
60348
60349     /**
60350      * <p>Returns true if any fields in this form have changed from their original values.</p>
60351      * <p>Note that if this BasicForm was configured with {@link #trackResetOnLoad} then the
60352      * Fields' <i>original values</i> are updated when the values are loaded by {@link #setValues}
60353      * or {@link #loadRecord}.</p>
60354      * @return Boolean
60355      */
60356     isDirty : function(){
60357         var dirty = false;
60358         this.items.each(function(f){
60359            if(f.isDirty()){
60360                dirty = true;
60361                return false;
60362            }
60363         });
60364         return dirty;
60365     },
60366
60367     /**
60368      * Performs a predefined action ({@link Ext.form.Action.Submit} or
60369      * {@link Ext.form.Action.Load}) or a custom extension of {@link Ext.form.Action}
60370      * to perform application-specific processing.
60371      * @param {String/Object} actionName The name of the predefined action type,
60372      * or instance of {@link Ext.form.Action} to perform.
60373      * @param {Object} options (optional) The options to pass to the {@link Ext.form.Action}.
60374      * All of the config options listed below are supported by both the
60375      * {@link Ext.form.Action.Submit submit} and {@link Ext.form.Action.Load load}
60376      * actions unless otherwise noted (custom actions could also accept
60377      * other config options):<ul>
60378      *
60379      * <li><b>url</b> : String<div class="sub-desc">The url for the action (defaults
60380      * to the form's {@link #url}.)</div></li>
60381      *
60382      * <li><b>method</b> : String<div class="sub-desc">The form method to use (defaults
60383      * to the form's method, or POST if not defined)</div></li>
60384      *
60385      * <li><b>params</b> : String/Object<div class="sub-desc"><p>The params to pass
60386      * (defaults to the form's baseParams, or none if not defined)</p>
60387      * <p>Parameters are encoded as standard HTTP parameters using {@link Ext#urlEncode}.</p></div></li>
60388      *
60389      * <li><b>headers</b> : Object<div class="sub-desc">Request headers to set for the action
60390      * (defaults to the form's default headers)</div></li>
60391      *
60392      * <li><b>success</b> : Function<div class="sub-desc">The callback that will
60393      * be invoked after a successful response (see top of
60394      * {@link Ext.form.Action.Submit submit} and {@link Ext.form.Action.Load load}
60395      * for a description of what constitutes a successful response).
60396      * The function is passed the following parameters:<ul>
60397      * <li><tt>form</tt> : Ext.form.BasicForm<div class="sub-desc">The form that requested the action</div></li>
60398      * <li><tt>action</tt> : The {@link Ext.form.Action Action} object which performed the operation.
60399      * <div class="sub-desc">The action object contains these properties of interest:<ul>
60400      * <li><tt>{@link Ext.form.Action#response response}</tt></li>
60401      * <li><tt>{@link Ext.form.Action#result result}</tt> : interrogate for custom postprocessing</li>
60402      * <li><tt>{@link Ext.form.Action#type type}</tt></li>
60403      * </ul></div></li></ul></div></li>
60404      *
60405      * <li><b>failure</b> : Function<div class="sub-desc">The callback that will be invoked after a
60406      * failed transaction attempt. The function is passed the following parameters:<ul>
60407      * <li><tt>form</tt> : The {@link Ext.form.BasicForm} that requested the action.</li>
60408      * <li><tt>action</tt> : The {@link Ext.form.Action Action} object which performed the operation.
60409      * <div class="sub-desc">The action object contains these properties of interest:<ul>
60410      * <li><tt>{@link Ext.form.Action#failureType failureType}</tt></li>
60411      * <li><tt>{@link Ext.form.Action#response response}</tt></li>
60412      * <li><tt>{@link Ext.form.Action#result result}</tt> : interrogate for custom postprocessing</li>
60413      * <li><tt>{@link Ext.form.Action#type type}</tt></li>
60414      * </ul></div></li></ul></div></li>
60415      *
60416      * <li><b>scope</b> : Object<div class="sub-desc">The scope in which to call the
60417      * callback functions (The <tt>this</tt> reference for the callback functions).</div></li>
60418      *
60419      * <li><b>clientValidation</b> : Boolean<div class="sub-desc">Submit Action only.
60420      * Determines whether a Form's fields are validated in a final call to
60421      * {@link Ext.form.BasicForm#isValid isValid} prior to submission. Set to <tt>false</tt>
60422      * to prevent this. If undefined, pre-submission field validation is performed.</div></li></ul>
60423      *
60424      * @return {BasicForm} this
60425      */
60426     doAction : function(action, options){
60427         if(Ext.isString(action)){
60428             action = new Ext.form.Action.ACTION_TYPES[action](this, options);
60429         }
60430         if(this.fireEvent('beforeaction', this, action) !== false){
60431             this.beforeAction(action);
60432             action.run.defer(100, action);
60433         }
60434         return this;
60435     },
60436
60437     /**
60438      * Shortcut to {@link #doAction do} a {@link Ext.form.Action.Submit submit action}.
60439      * @param {Object} options The options to pass to the action (see {@link #doAction} for details).<br>
60440      * <p><b>Note:</b> this is ignored when using the {@link #standardSubmit} option.</p>
60441      * <p>The following code:</p><pre><code>
60442 myFormPanel.getForm().submit({
60443     clientValidation: true,
60444     url: 'updateConsignment.php',
60445     params: {
60446         newStatus: 'delivered'
60447     },
60448     success: function(form, action) {
60449        Ext.Msg.alert('Success', action.result.msg);
60450     },
60451     failure: function(form, action) {
60452         switch (action.failureType) {
60453             case Ext.form.Action.CLIENT_INVALID:
60454                 Ext.Msg.alert('Failure', 'Form fields may not be submitted with invalid values');
60455                 break;
60456             case Ext.form.Action.CONNECT_FAILURE:
60457                 Ext.Msg.alert('Failure', 'Ajax communication failed');
60458                 break;
60459             case Ext.form.Action.SERVER_INVALID:
60460                Ext.Msg.alert('Failure', action.result.msg);
60461        }
60462     }
60463 });
60464 </code></pre>
60465      * would process the following server response for a successful submission:<pre><code>
60466 {
60467     "success":true, // note this is Boolean, not string
60468     "msg":"Consignment updated"
60469 }
60470 </code></pre>
60471      * and the following server response for a failed submission:<pre><code>
60472 {
60473     "success":false, // note this is Boolean, not string
60474     "msg":"You do not have permission to perform this operation"
60475 }
60476 </code></pre>
60477      * @return {BasicForm} this
60478      */
60479     submit : function(options){
60480         if(this.standardSubmit){
60481             var v = this.isValid();
60482             if(v){
60483                 var el = this.el.dom;
60484                 if(this.url && Ext.isEmpty(el.action)){
60485                     el.action = this.url;
60486                 }
60487                 el.submit();
60488             }
60489             return v;
60490         }
60491         var submitAction = String.format('{0}submit', this.api ? 'direct' : '');
60492         this.doAction(submitAction, options);
60493         return this;
60494     },
60495
60496     /**
60497      * Shortcut to {@link #doAction do} a {@link Ext.form.Action.Load load action}.
60498      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
60499      * @return {BasicForm} this
60500      */
60501     load : function(options){
60502         var loadAction = String.format('{0}load', this.api ? 'direct' : '');
60503         this.doAction(loadAction, options);
60504         return this;
60505     },
60506
60507     /**
60508      * Persists the values in this form into the passed {@link Ext.data.Record} object in a beginEdit/endEdit block.
60509      * @param {Record} record The record to edit
60510      * @return {BasicForm} this
60511      */
60512     updateRecord : function(record){
60513         record.beginEdit();
60514         var fs = record.fields;
60515         fs.each(function(f){
60516             var field = this.findField(f.name);
60517             if(field){
60518                 record.set(f.name, field.getValue());
60519             }
60520         }, this);
60521         record.endEdit();
60522         return this;
60523     },
60524
60525     /**
60526      * Loads an {@link Ext.data.Record} into this form by calling {@link #setValues} with the
60527      * {@link Ext.data.Record#data record data}.
60528      * See also {@link #trackResetOnLoad}.
60529      * @param {Record} record The record to load
60530      * @return {BasicForm} this
60531      */
60532     loadRecord : function(record){
60533         this.setValues(record.data);
60534         return this;
60535     },
60536
60537     // private
60538     beforeAction : function(action){
60539         var o = action.options;
60540         if(o.waitMsg){
60541             if(this.waitMsgTarget === true){
60542                 this.el.mask(o.waitMsg, 'x-mask-loading');
60543             }else if(this.waitMsgTarget){
60544                 this.waitMsgTarget = Ext.get(this.waitMsgTarget);
60545                 this.waitMsgTarget.mask(o.waitMsg, 'x-mask-loading');
60546             }else{
60547                 Ext.MessageBox.wait(o.waitMsg, o.waitTitle || this.waitTitle);
60548             }
60549         }
60550     },
60551
60552     // private
60553     afterAction : function(action, success){
60554         this.activeAction = null;
60555         var o = action.options;
60556         if(o.waitMsg){
60557             if(this.waitMsgTarget === true){
60558                 this.el.unmask();
60559             }else if(this.waitMsgTarget){
60560                 this.waitMsgTarget.unmask();
60561             }else{
60562                 Ext.MessageBox.updateProgress(1);
60563                 Ext.MessageBox.hide();
60564             }
60565         }
60566         if(success){
60567             if(o.reset){
60568                 this.reset();
60569             }
60570             Ext.callback(o.success, o.scope, [this, action]);
60571             this.fireEvent('actioncomplete', this, action);
60572         }else{
60573             Ext.callback(o.failure, o.scope, [this, action]);
60574             this.fireEvent('actionfailed', this, action);
60575         }
60576     },
60577
60578     /**
60579      * Find a {@link Ext.form.Field} in this form.
60580      * @param {String} id The value to search for (specify either a {@link Ext.Component#id id},
60581      * {@link Ext.grid.Column#dataIndex dataIndex}, {@link Ext.form.Field#getName name or hiddenName}).
60582      * @return Field
60583      */
60584     findField : function(id){
60585         var field = this.items.get(id);
60586         if(!Ext.isObject(field)){
60587             this.items.each(function(f){
60588                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
60589                     field = f;
60590                     return false;
60591                 }
60592             });
60593         }
60594         return field || null;
60595     },
60596
60597
60598     /**
60599      * Mark fields in this form invalid in bulk.
60600      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
60601      * @return {BasicForm} this
60602      */
60603     markInvalid : function(errors){
60604         if(Ext.isArray(errors)){
60605             for(var i = 0, len = errors.length; i < len; i++){
60606                 var fieldError = errors[i];
60607                 var f = this.findField(fieldError.id);
60608                 if(f){
60609                     f.markInvalid(fieldError.msg);
60610                 }
60611             }
60612         }else{
60613             var field, id;
60614             for(id in errors){
60615                 if(!Ext.isFunction(errors[id]) && (field = this.findField(id))){
60616                     field.markInvalid(errors[id]);
60617                 }
60618             }
60619         }
60620         return this;
60621     },
60622
60623     /**
60624      * Set values for fields in this form in bulk.
60625      * @param {Array/Object} values Either an array in the form:<pre><code>
60626 [{id:'clientName', value:'Fred. Olsen Lines'},
60627  {id:'portOfLoading', value:'FXT'},
60628  {id:'portOfDischarge', value:'OSL'} ]</code></pre>
60629      * or an object hash of the form:<pre><code>
60630 {
60631     clientName: 'Fred. Olsen Lines',
60632     portOfLoading: 'FXT',
60633     portOfDischarge: 'OSL'
60634 }</code></pre>
60635      * @return {BasicForm} this
60636      */
60637     setValues : function(values){
60638         if(Ext.isArray(values)){ // array of objects
60639             for(var i = 0, len = values.length; i < len; i++){
60640                 var v = values[i];
60641                 var f = this.findField(v.id);
60642                 if(f){
60643                     f.setValue(v.value);
60644                     if(this.trackResetOnLoad){
60645                         f.originalValue = f.getValue();
60646                     }
60647                 }
60648             }
60649         }else{ // object hash
60650             var field, id;
60651             for(id in values){
60652                 if(!Ext.isFunction(values[id]) && (field = this.findField(id))){
60653                     field.setValue(values[id]);
60654                     if(this.trackResetOnLoad){
60655                         field.originalValue = field.getValue();
60656                     }
60657                 }
60658             }
60659         }
60660         return this;
60661     },
60662
60663     /**
60664      * <p>Returns the fields in this form as an object with key/value pairs as they would be submitted using a standard form submit.
60665      * If multiple fields exist with the same name they are returned as an array.</p>
60666      * <p><b>Note:</b> The values are collected from all enabled HTML input elements within the form, <u>not</u> from
60667      * the Ext Field objects. This means that all returned values are Strings (or Arrays of Strings) and that the
60668      * value can potentially be the emptyText of a field.</p>
60669      * @param {Boolean} asString (optional) Pass true to return the values as a string. (defaults to false, returning an Object)
60670      * @return {String/Object}
60671      */
60672     getValues : function(asString){
60673         var fs = Ext.lib.Ajax.serializeForm(this.el.dom);
60674         if(asString === true){
60675             return fs;
60676         }
60677         return Ext.urlDecode(fs);
60678     },
60679
60680     /**
60681      * Retrieves the fields in the form as a set of key/value pairs, using the {@link Ext.form.Field#getValue getValue()} method.
60682      * If multiple fields exist with the same name they are returned as an array.
60683      * @param {Boolean} dirtyOnly (optional) True to return only fields that are dirty.
60684      * @return {Object} The values in the form
60685      */
60686     getFieldValues : function(dirtyOnly){
60687         var o = {},
60688             n,
60689             key,
60690             val;
60691         this.items.each(function(f){
60692             if(dirtyOnly !== true || f.isDirty()){
60693                 n = f.getName();
60694                 key = o[n];
60695                 val = f.getValue();
60696                 
60697                 if(Ext.isDefined(key)){
60698                     if(Ext.isArray(key)){
60699                         o[n].push(val);
60700                     }else{
60701                         o[n] = [key, val];
60702                     }
60703                 }else{
60704                     o[n] = val;
60705                 }
60706             }
60707         });
60708         return o;
60709     },
60710
60711     /**
60712      * Clears all invalid messages in this form.
60713      * @return {BasicForm} this
60714      */
60715     clearInvalid : function(){
60716         this.items.each(function(f){
60717            f.clearInvalid();
60718         });
60719         return this;
60720     },
60721
60722     /**
60723      * Resets this form.
60724      * @return {BasicForm} this
60725      */
60726     reset : function(){
60727         this.items.each(function(f){
60728             f.reset();
60729         });
60730         return this;
60731     },
60732
60733     /**
60734      * Add Ext.form Components to this form's Collection. This does not result in rendering of
60735      * the passed Component, it just enables the form to validate Fields, and distribute values to
60736      * Fields.
60737      * <p><b>You will not usually call this function. In order to be rendered, a Field must be added
60738      * to a {@link Ext.Container Container}, usually an {@link Ext.form.FormPanel FormPanel}.
60739      * The FormPanel to which the field is added takes care of adding the Field to the BasicForm's
60740      * collection.</b></p>
60741      * @param {Field} field1
60742      * @param {Field} field2 (optional)
60743      * @param {Field} etc (optional)
60744      * @return {BasicForm} this
60745      */
60746     add : function(){
60747         this.items.addAll(Array.prototype.slice.call(arguments, 0));
60748         return this;
60749     },
60750
60751
60752     /**
60753      * Removes a field from the items collection (does NOT remove its markup).
60754      * @param {Field} field
60755      * @return {BasicForm} this
60756      */
60757     remove : function(field){
60758         this.items.remove(field);
60759         return this;
60760     },
60761
60762     /**
60763      * Iterates through the {@link Ext.form.Field Field}s which have been {@link #add add}ed to this BasicForm,
60764      * checks them for an id attribute, and calls {@link Ext.form.Field#applyToMarkup} on the existing dom element with that id.
60765      * @return {BasicForm} this
60766      */
60767     render : function(){
60768         this.items.each(function(f){
60769             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
60770                 f.applyToMarkup(f.id);
60771             }
60772         });
60773         return this;
60774     },
60775
60776     /**
60777      * Calls {@link Ext#apply} for all fields in this form with the passed object.
60778      * @param {Object} values
60779      * @return {BasicForm} this
60780      */
60781     applyToFields : function(o){
60782         this.items.each(function(f){
60783            Ext.apply(f, o);
60784         });
60785         return this;
60786     },
60787
60788     /**
60789      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
60790      * @param {Object} values
60791      * @return {BasicForm} this
60792      */
60793     applyIfToFields : function(o){
60794         this.items.each(function(f){
60795            Ext.applyIf(f, o);
60796         });
60797         return this;
60798     },
60799
60800     callFieldMethod : function(fnName, args){
60801         args = args || [];
60802         this.items.each(function(f){
60803             if(Ext.isFunction(f[fnName])){
60804                 f[fnName].apply(f, args);
60805             }
60806         });
60807         return this;
60808     }
60809 });
60810
60811 // back compat
60812 Ext.BasicForm = Ext.form.BasicForm;/**
60813  * @class Ext.form.FormPanel
60814  * @extends Ext.Panel
60815  * <p>Standard form container.</p>
60816  *
60817  * <p><b><u>Layout</u></b></p>
60818  * <p>By default, FormPanel is configured with <tt>layout:'form'</tt> to use an {@link Ext.layout.FormLayout}
60819  * layout manager, which styles and renders fields and labels correctly. When nesting additional Containers
60820  * within a FormPanel, you should ensure that any descendant Containers which host input Fields use the
60821  * {@link Ext.layout.FormLayout} layout manager.</p>
60822  *
60823  * <p><b><u>BasicForm</u></b></p>
60824  * <p>Although <b>not listed</b> as configuration options of FormPanel, the FormPanel class accepts all
60825  * of the config options required to configure its internal {@link Ext.form.BasicForm} for:
60826  * <div class="mdetail-params"><ul>
60827  * <li>{@link Ext.form.BasicForm#fileUpload file uploads}</li>
60828  * <li>functionality for {@link Ext.form.BasicForm#doAction loading, validating and submitting} the form</li>
60829  * </ul></div>
60830  *
60831  * <p><b>Note</b>: If subclassing FormPanel, any configuration options for the BasicForm must be applied to
60832  * the <tt><b>initialConfig</b></tt> property of the FormPanel. Applying {@link Ext.form.BasicForm BasicForm}
60833  * configuration settings to <b><tt>this</tt></b> will <b>not</b> affect the BasicForm's configuration.</p>
60834  *
60835  * <p><b><u>Form Validation</u></b></p>
60836  * <p>For information on form validation see the following:</p>
60837  * <div class="mdetail-params"><ul>
60838  * <li>{@link Ext.form.TextField}</li>
60839  * <li>{@link Ext.form.VTypes}</li>
60840  * <li>{@link Ext.form.BasicForm#doAction BasicForm.doAction <b>clientValidation</b> notes}</li>
60841  * <li><tt>{@link Ext.form.FormPanel#monitorValid monitorValid}</tt></li>
60842  * </ul></div>
60843  *
60844  * <p><b><u>Form Submission</u></b></p>
60845  * <p>By default, Ext Forms are submitted through Ajax, using {@link Ext.form.Action}. To enable normal browser
60846  * submission of the {@link Ext.form.BasicForm BasicForm} contained in this FormPanel, see the
60847  * <tt><b>{@link Ext.form.BasicForm#standardSubmit standardSubmit}</b></tt> option.</p>
60848  *
60849  * @constructor
60850  * @param {Object} config Configuration options
60851  * @xtype form
60852  */
60853 Ext.FormPanel = Ext.extend(Ext.Panel, {
60854     /**
60855      * @cfg {String} formId (optional) The id of the FORM tag (defaults to an auto-generated id).
60856      */
60857     /**
60858      * @cfg {Boolean} hideLabels
60859      * <p><tt>true</tt> to hide field labels by default (sets <tt>display:none</tt>). Defaults to
60860      * <tt>false</tt>.</p>
60861      * <p>Also see {@link Ext.Component}.<tt>{@link Ext.Component#hideLabel hideLabel}</tt>.
60862      */
60863     /**
60864      * @cfg {Number} labelPad
60865      * The default padding in pixels for field labels (defaults to <tt>5</tt>). <tt>labelPad</tt> only
60866      * applies if <tt>{@link #labelWidth}</tt> is also specified, otherwise it will be ignored.
60867      */
60868     /**
60869      * @cfg {String} labelSeparator
60870      * See {@link Ext.Component}.<tt>{@link Ext.Component#labelSeparator labelSeparator}</tt>
60871      */
60872     /**
60873      * @cfg {Number} labelWidth The width of labels in pixels. This property cascades to child containers
60874      * and can be overridden on any child container (e.g., a fieldset can specify a different <tt>labelWidth</tt>
60875      * for its fields) (defaults to <tt>100</tt>).
60876      */
60877     /**
60878      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
60879      */
60880     /**
60881      * @cfg {Array} buttons
60882      * An array of {@link Ext.Button}s or {@link Ext.Button} configs used to add buttons to the footer of this FormPanel.<br>
60883      * <p>Buttons in the footer of a FormPanel may be configured with the option <tt>formBind: true</tt>. This causes
60884      * the form's {@link #monitorValid valid state monitor task} to enable/disable those Buttons depending on
60885      * the form's valid/invalid state.</p>
60886      */
60887
60888
60889     /**
60890      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to <tt>75</tt>).
60891      */
60892     minButtonWidth : 75,
60893
60894     /**
60895      * @cfg {String} labelAlign The label alignment value used for the <tt>text-align</tt> specification
60896      * for the <b>container</b>. Valid values are <tt>"left</tt>", <tt>"top"</tt> or <tt>"right"</tt>
60897      * (defaults to <tt>"left"</tt>). This property cascades to child <b>containers</b> and can be
60898      * overridden on any child <b>container</b> (e.g., a fieldset can specify a different <tt>labelAlign</tt>
60899      * for its fields).
60900      */
60901     labelAlign : 'left',
60902
60903     /**
60904      * @cfg {Boolean} monitorValid If <tt>true</tt>, the form monitors its valid state <b>client-side</b> and
60905      * regularly fires the {@link #clientvalidation} event passing that state.<br>
60906      * <p>When monitoring valid state, the FormPanel enables/disables any of its configured
60907      * {@link #buttons} which have been configured with <code>formBind: true</code> depending
60908      * on whether the {@link Ext.form.BasicForm#isValid form is valid} or not. Defaults to <tt>false</tt></p>
60909      */
60910     monitorValid : false,
60911
60912     /**
60913      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
60914      */
60915     monitorPoll : 200,
60916
60917     /**
60918      * @cfg {String} layout Defaults to <tt>'form'</tt>.  Normally this configuration property should not be altered.
60919      * For additional details see {@link Ext.layout.FormLayout} and {@link Ext.Container#layout Ext.Container.layout}.
60920      */
60921     layout : 'form',
60922
60923     // private
60924     initComponent : function(){
60925         this.form = this.createForm();
60926         Ext.FormPanel.superclass.initComponent.call(this);
60927
60928         this.bodyCfg = {
60929             tag: 'form',
60930             cls: this.baseCls + '-body',
60931             method : this.method || 'POST',
60932             id : this.formId || Ext.id()
60933         };
60934         if(this.fileUpload) {
60935             this.bodyCfg.enctype = 'multipart/form-data';
60936         }
60937         this.initItems();
60938
60939         this.addEvents(
60940             /**
60941              * @event clientvalidation
60942              * If the monitorValid config option is true, this event fires repetitively to notify of valid state
60943              * @param {Ext.form.FormPanel} this
60944              * @param {Boolean} valid true if the form has passed client-side validation
60945              */
60946             'clientvalidation'
60947         );
60948
60949         this.relayEvents(this.form, ['beforeaction', 'actionfailed', 'actioncomplete']);
60950     },
60951
60952     // private
60953     createForm : function(){
60954         var config = Ext.applyIf({listeners: {}}, this.initialConfig);
60955         return new Ext.form.BasicForm(null, config);
60956     },
60957
60958     // private
60959     initFields : function(){
60960         var f = this.form;
60961         var formPanel = this;
60962         var fn = function(c){
60963             if(formPanel.isField(c)){
60964                 f.add(c);
60965             }else if(c.findBy && c != formPanel){
60966                 formPanel.applySettings(c);
60967                 //each check required for check/radio groups.
60968                 if(c.items && c.items.each){
60969                     c.items.each(fn, this);
60970                 }
60971             }
60972         };
60973         this.items.each(fn, this);
60974     },
60975
60976     // private
60977     applySettings: function(c){
60978         var ct = c.ownerCt;
60979         Ext.applyIf(c, {
60980             labelAlign: ct.labelAlign,
60981             labelWidth: ct.labelWidth,
60982             itemCls: ct.itemCls
60983         });
60984     },
60985
60986     // private
60987     getLayoutTarget : function(){
60988         return this.form.el;
60989     },
60990
60991     /**
60992      * Provides access to the {@link Ext.form.BasicForm Form} which this Panel contains.
60993      * @return {Ext.form.BasicForm} The {@link Ext.form.BasicForm Form} which this Panel contains.
60994      */
60995     getForm : function(){
60996         return this.form;
60997     },
60998
60999     // private
61000     onRender : function(ct, position){
61001         this.initFields();
61002         Ext.FormPanel.superclass.onRender.call(this, ct, position);
61003         this.form.initEl(this.body);
61004     },
61005
61006     // private
61007     beforeDestroy : function(){
61008         this.stopMonitoring();
61009         /*
61010          * Don't move this behaviour to BasicForm because it can be used
61011          * on it's own.
61012          */
61013         Ext.destroy(this.form);
61014         this.form.items.clear();
61015         Ext.FormPanel.superclass.beforeDestroy.call(this);
61016     },
61017
61018     // Determine if a Component is usable as a form Field.
61019     isField : function(c) {
61020         return !!c.setValue && !!c.getValue && !!c.markInvalid && !!c.clearInvalid;
61021     },
61022
61023     // private
61024     initEvents : function(){
61025         Ext.FormPanel.superclass.initEvents.call(this);
61026         // Listeners are required here to catch bubbling events from children.
61027         this.on({
61028             scope: this,
61029             add: this.onAddEvent,
61030             remove: this.onRemoveEvent
61031         });
61032         if(this.monitorValid){ // initialize after render
61033             this.startMonitoring();
61034         }
61035     },
61036
61037     // private
61038     onAdd: function(c){
61039         Ext.FormPanel.superclass.onAdd.call(this, c);
61040         this.processAdd(c);
61041     },
61042
61043     // private
61044     onAddEvent: function(ct, c){
61045         if(ct !== this){
61046             this.processAdd(c);
61047         }
61048     },
61049
61050     // private
61051     processAdd : function(c){
61052         // If a single form Field, add it
61053         if(this.isField(c)){
61054             this.form.add(c);
61055         // If a Container, add any Fields it might contain
61056         }else if(c.findBy){
61057             this.applySettings(c);
61058             this.form.add.apply(this.form, c.findBy(this.isField));
61059         }
61060     },
61061
61062     // private
61063     onRemove: function(c){
61064         Ext.FormPanel.superclass.onRemove.call(this, c);
61065         this.processRemove(c);
61066     },
61067
61068     onRemoveEvent: function(ct, c){
61069         if(ct !== this){
61070             this.processRemove(c);
61071         }
61072     },
61073
61074     // private
61075     processRemove : function(c){
61076         // If a single form Field, remove it
61077         if(this.isField(c)){
61078             this.form.remove(c);
61079         // If a Container, its already destroyed by the time it gets here.  Remove any references to destroyed fields.
61080         }else if(c.findBy){
61081             var isDestroyed = function(o) {
61082                 return !!o.isDestroyed;
61083             }
61084             this.form.items.filterBy(isDestroyed, this.form).each(this.form.remove, this.form);
61085         }
61086     },
61087
61088     /**
61089      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
61090      * option "monitorValid"
61091      */
61092     startMonitoring : function(){
61093         if(!this.validTask){
61094             this.validTask = new Ext.util.TaskRunner();
61095             this.validTask.start({
61096                 run : this.bindHandler,
61097                 interval : this.monitorPoll || 200,
61098                 scope: this
61099             });
61100         }
61101     },
61102
61103     /**
61104      * Stops monitoring of the valid state of this form
61105      */
61106     stopMonitoring : function(){
61107         if(this.validTask){
61108             this.validTask.stopAll();
61109             this.validTask = null;
61110         }
61111     },
61112
61113     /**
61114      * This is a proxy for the underlying BasicForm's {@link Ext.form.BasicForm#load} call.
61115      * @param {Object} options The options to pass to the action (see {@link Ext.form.BasicForm#doAction} for details)
61116      */
61117     load : function(){
61118         this.form.load.apply(this.form, arguments);
61119     },
61120
61121     // private
61122     onDisable : function(){
61123         Ext.FormPanel.superclass.onDisable.call(this);
61124         if(this.form){
61125             this.form.items.each(function(){
61126                  this.disable();
61127             });
61128         }
61129     },
61130
61131     // private
61132     onEnable : function(){
61133         Ext.FormPanel.superclass.onEnable.call(this);
61134         if(this.form){
61135             this.form.items.each(function(){
61136                  this.enable();
61137             });
61138         }
61139     },
61140
61141     // private
61142     bindHandler : function(){
61143         var valid = true;
61144         this.form.items.each(function(f){
61145             if(!f.isValid(true)){
61146                 valid = false;
61147                 return false;
61148             }
61149         });
61150         if(this.fbar){
61151             var fitems = this.fbar.items.items;
61152             for(var i = 0, len = fitems.length; i < len; i++){
61153                 var btn = fitems[i];
61154                 if(btn.formBind === true && btn.disabled === valid){
61155                     btn.setDisabled(!valid);
61156                 }
61157             }
61158         }
61159         this.fireEvent('clientvalidation', this, valid);
61160     }
61161 });
61162 Ext.reg('form', Ext.FormPanel);
61163
61164 Ext.form.FormPanel = Ext.FormPanel;
61165 /**\r
61166  * @class Ext.form.FieldSet\r
61167  * @extends Ext.Panel\r
61168  * Standard container used for grouping items within a {@link Ext.form.FormPanel form}.\r
61169  * <pre><code>\r
61170 var form = new Ext.FormPanel({\r
61171     title: 'Simple Form with FieldSets',\r
61172     labelWidth: 75, // label settings here cascade unless overridden\r
61173     url: 'save-form.php',\r
61174     frame:true,\r
61175     bodyStyle:'padding:5px 5px 0',\r
61176     width: 700,\r
61177     renderTo: document.body,\r
61178     layout:'column', // arrange items in columns\r
61179     defaults: {      // defaults applied to items\r
61180         layout: 'form',\r
61181         border: false,\r
61182         bodyStyle: 'padding:4px'\r
61183     },\r
61184     items: [{\r
61185         // Fieldset in Column 1\r
61186         xtype:'fieldset',\r
61187         columnWidth: 0.5,\r
61188         title: 'Fieldset 1',\r
61189         collapsible: true,\r
61190         autoHeight:true,\r
61191         defaults: {\r
61192             anchor: '-20' // leave room for error icon\r
61193         },\r
61194         defaultType: 'textfield',\r
61195         items :[{\r
61196                 fieldLabel: 'Field 1'\r
61197             }, {\r
61198                 fieldLabel: 'Field 2'\r
61199             }, {\r
61200                 fieldLabel: 'Field 3'\r
61201             }\r
61202         ]\r
61203     },{\r
61204         // Fieldset in Column 2 - Panel inside\r
61205         xtype:'fieldset',\r
61206         title: 'Show Panel', // title, header, or checkboxToggle creates fieldset header\r
61207         autoHeight:true,\r
61208         columnWidth: 0.5,\r
61209         checkboxToggle: true,\r
61210         collapsed: true, // fieldset initially collapsed\r
61211         layout:'anchor',\r
61212         items :[{\r
61213             xtype: 'panel',\r
61214             anchor: '100%',\r
61215             title: 'Panel inside a fieldset',\r
61216             frame: true,\r
61217             height: 100\r
61218         }]\r
61219     }]\r
61220 });\r
61221  * </code></pre>\r
61222  * @constructor\r
61223  * @param {Object} config Configuration options\r
61224  * @xtype fieldset\r
61225  */\r
61226 Ext.form.FieldSet = Ext.extend(Ext.Panel, {\r
61227     /**\r
61228      * @cfg {Mixed} checkboxToggle <tt>true</tt> to render a checkbox into the fieldset frame just\r
61229      * in front of the legend to expand/collapse the fieldset when the checkbox is toggled. (defaults\r
61230      * to <tt>false</tt>).\r
61231      * <p>A {@link Ext.DomHelper DomHelper} element spec may also be specified to create the checkbox.\r
61232      * If <tt>true</tt> is specified, the default DomHelper config object used to create the element\r
61233      * is:</p><pre><code>\r
61234      * {tag: 'input', type: 'checkbox', name: this.checkboxName || this.id+'-checkbox'}\r
61235      * </code></pre>   \r
61236      */\r
61237     /**\r
61238      * @cfg {String} checkboxName The name to assign to the fieldset's checkbox if <tt>{@link #checkboxToggle} = true</tt>\r
61239      * (defaults to <tt>'[checkbox id]-checkbox'</tt>).\r
61240      */\r
61241     /**\r
61242      * @cfg {Boolean} collapsible\r
61243      * <tt>true</tt> to make the fieldset collapsible and have the expand/collapse toggle button automatically\r
61244      * rendered into the legend element, <tt>false</tt> to keep the fieldset statically sized with no collapse\r
61245      * button (defaults to <tt>false</tt>). Another option is to configure <tt>{@link #checkboxToggle}</tt>.\r
61246      */\r
61247     /**\r
61248      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.\r
61249      */\r
61250     /**\r
61251      * @cfg {String} itemCls A css class to apply to the <tt>x-form-item</tt> of fields (see \r
61252      * {@link Ext.layout.FormLayout}.{@link Ext.layout.FormLayout#fieldTpl fieldTpl} for details).\r
61253      * This property cascades to child containers.\r
61254      */\r
61255     /**\r
61256      * @cfg {String} baseCls The base CSS class applied to the fieldset (defaults to <tt>'x-fieldset'</tt>).\r
61257      */\r
61258     baseCls : 'x-fieldset',\r
61259     /**\r
61260      * @cfg {String} layout The {@link Ext.Container#layout} to use inside the fieldset (defaults to <tt>'form'</tt>).\r
61261      */\r
61262     layout : 'form',\r
61263     /**\r
61264      * @cfg {Boolean} animCollapse\r
61265      * <tt>true</tt> to animate the transition when the panel is collapsed, <tt>false</tt> to skip the\r
61266      * animation (defaults to <tt>false</tt>).\r
61267      */\r
61268     animCollapse : false,\r
61269 \r
61270     // private\r
61271     onRender : function(ct, position){\r
61272         if(!this.el){\r
61273             this.el = document.createElement('fieldset');\r
61274             this.el.id = this.id;\r
61275             if (this.title || this.header || this.checkboxToggle) {\r
61276                 this.el.appendChild(document.createElement('legend')).className = this.baseCls + '-header';\r
61277             }\r
61278         }\r
61279 \r
61280         Ext.form.FieldSet.superclass.onRender.call(this, ct, position);\r
61281 \r
61282         if(this.checkboxToggle){\r
61283             var o = typeof this.checkboxToggle == 'object' ?\r
61284                     this.checkboxToggle :\r
61285                     {tag: 'input', type: 'checkbox', name: this.checkboxName || this.id+'-checkbox'};\r
61286             this.checkbox = this.header.insertFirst(o);\r
61287             this.checkbox.dom.checked = !this.collapsed;\r
61288             this.mon(this.checkbox, 'click', this.onCheckClick, this);\r
61289         }\r
61290     },\r
61291 \r
61292     // private\r
61293     onCollapse : function(doAnim, animArg){\r
61294         if(this.checkbox){\r
61295             this.checkbox.dom.checked = false;\r
61296         }\r
61297         Ext.form.FieldSet.superclass.onCollapse.call(this, doAnim, animArg);\r
61298 \r
61299     },\r
61300 \r
61301     // private\r
61302     onExpand : function(doAnim, animArg){\r
61303         if(this.checkbox){\r
61304             this.checkbox.dom.checked = true;\r
61305         }\r
61306         Ext.form.FieldSet.superclass.onExpand.call(this, doAnim, animArg);\r
61307     },\r
61308 \r
61309     /**\r
61310      * This function is called by the fieldset's checkbox when it is toggled (only applies when\r
61311      * checkboxToggle = true).  This method should never be called externally, but can be\r
61312      * overridden to provide custom behavior when the checkbox is toggled if needed.\r
61313      */\r
61314     onCheckClick : function(){\r
61315         this[this.checkbox.dom.checked ? 'expand' : 'collapse']();\r
61316     }\r
61317 \r
61318     /**\r
61319      * @cfg {String/Number} activeItem\r
61320      * @hide\r
61321      */\r
61322     /**\r
61323      * @cfg {Mixed} applyTo\r
61324      * @hide\r
61325      */\r
61326     /**\r
61327      * @cfg {Boolean} bodyBorder\r
61328      * @hide\r
61329      */\r
61330     /**\r
61331      * @cfg {Boolean} border\r
61332      * @hide\r
61333      */\r
61334     /**\r
61335      * @cfg {Boolean/Number} bufferResize\r
61336      * @hide\r
61337      */\r
61338     /**\r
61339      * @cfg {Boolean} collapseFirst\r
61340      * @hide\r
61341      */\r
61342     /**\r
61343      * @cfg {String} defaultType\r
61344      * @hide\r
61345      */\r
61346     /**\r
61347      * @cfg {String} disabledClass\r
61348      * @hide\r
61349      */\r
61350     /**\r
61351      * @cfg {String} elements\r
61352      * @hide\r
61353      */\r
61354     /**\r
61355      * @cfg {Boolean} floating\r
61356      * @hide\r
61357      */\r
61358     /**\r
61359      * @cfg {Boolean} footer\r
61360      * @hide\r
61361      */\r
61362     /**\r
61363      * @cfg {Boolean} frame\r
61364      * @hide\r
61365      */\r
61366     /**\r
61367      * @cfg {Boolean} header\r
61368      * @hide\r
61369      */\r
61370     /**\r
61371      * @cfg {Boolean} headerAsText\r
61372      * @hide\r
61373      */\r
61374     /**\r
61375      * @cfg {Boolean} hideCollapseTool\r
61376      * @hide\r
61377      */\r
61378     /**\r
61379      * @cfg {String} iconCls\r
61380      * @hide\r
61381      */\r
61382     /**\r
61383      * @cfg {Boolean/String} shadow\r
61384      * @hide\r
61385      */\r
61386     /**\r
61387      * @cfg {Number} shadowOffset\r
61388      * @hide\r
61389      */\r
61390     /**\r
61391      * @cfg {Boolean} shim\r
61392      * @hide\r
61393      */\r
61394     /**\r
61395      * @cfg {Object/Array} tbar\r
61396      * @hide\r
61397      */\r
61398     /**\r
61399      * @cfg {Array} tools\r
61400      * @hide\r
61401      */\r
61402     /**\r
61403      * @cfg {Ext.Template/Ext.XTemplate} toolTemplate\r
61404      * @hide\r
61405      */\r
61406     /**\r
61407      * @cfg {String} xtype\r
61408      * @hide\r
61409      */\r
61410     /**\r
61411      * @property header\r
61412      * @hide\r
61413      */\r
61414     /**\r
61415      * @property footer\r
61416      * @hide\r
61417      */\r
61418     /**\r
61419      * @method focus\r
61420      * @hide\r
61421      */\r
61422     /**\r
61423      * @method getBottomToolbar\r
61424      * @hide\r
61425      */\r
61426     /**\r
61427      * @method getTopToolbar\r
61428      * @hide\r
61429      */\r
61430     /**\r
61431      * @method setIconClass\r
61432      * @hide\r
61433      */\r
61434     /**\r
61435      * @event activate\r
61436      * @hide\r
61437      */\r
61438     /**\r
61439      * @event beforeclose\r
61440      * @hide\r
61441      */\r
61442     /**\r
61443      * @event bodyresize\r
61444      * @hide\r
61445      */\r
61446     /**\r
61447      * @event close\r
61448      * @hide\r
61449      */\r
61450     /**\r
61451      * @event deactivate\r
61452      * @hide\r
61453      */\r
61454 });\r
61455 Ext.reg('fieldset', Ext.form.FieldSet);\r
61456 /**
61457  * @class Ext.form.HtmlEditor
61458  * @extends Ext.form.Field
61459  * Provides a lightweight HTML Editor component. Some toolbar features are not supported by Safari and will be
61460  * automatically hidden when needed.  These are noted in the config options where appropriate.
61461  * <br><br>The editor's toolbar buttons have tooltips defined in the {@link #buttonTips} property, but they are not
61462  * enabled by default unless the global {@link Ext.QuickTips} singleton is {@link Ext.QuickTips#init initialized}.
61463  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
61464  * supported by this editor.</b>
61465  * <br><br>An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
61466  * any element that has display set to 'none' can cause problems in Safari and Firefox due to their default iframe reloading bugs.
61467  * <br><br>Example usage:
61468  * <pre><code>
61469 // Simple example rendered with default options:
61470 Ext.QuickTips.init();  // enable tooltips
61471 new Ext.form.HtmlEditor({
61472     renderTo: Ext.getBody(),
61473     width: 800,
61474     height: 300
61475 });
61476
61477 // Passed via xtype into a container and with custom options:
61478 Ext.QuickTips.init();  // enable tooltips
61479 new Ext.Panel({
61480     title: 'HTML Editor',
61481     renderTo: Ext.getBody(),
61482     width: 600,
61483     height: 300,
61484     frame: true,
61485     layout: 'fit',
61486     items: {
61487         xtype: 'htmleditor',
61488         enableColors: false,
61489         enableAlignments: false
61490     }
61491 });
61492 </code></pre>
61493  * @constructor
61494  * Create a new HtmlEditor
61495  * @param {Object} config
61496  * @xtype htmleditor
61497  */
61498
61499 Ext.form.HtmlEditor = Ext.extend(Ext.form.Field, {
61500     /**
61501      * @cfg {Boolean} enableFormat Enable the bold, italic and underline buttons (defaults to true)
61502      */
61503     enableFormat : true,
61504     /**
61505      * @cfg {Boolean} enableFontSize Enable the increase/decrease font size buttons (defaults to true)
61506      */
61507     enableFontSize : true,
61508     /**
61509      * @cfg {Boolean} enableColors Enable the fore/highlight color buttons (defaults to true)
61510      */
61511     enableColors : true,
61512     /**
61513      * @cfg {Boolean} enableAlignments Enable the left, center, right alignment buttons (defaults to true)
61514      */
61515     enableAlignments : true,
61516     /**
61517      * @cfg {Boolean} enableLists Enable the bullet and numbered list buttons. Not available in Safari. (defaults to true)
61518      */
61519     enableLists : true,
61520     /**
61521      * @cfg {Boolean} enableSourceEdit Enable the switch to source edit button. Not available in Safari. (defaults to true)
61522      */
61523     enableSourceEdit : true,
61524     /**
61525      * @cfg {Boolean} enableLinks Enable the create link button. Not available in Safari. (defaults to true)
61526      */
61527     enableLinks : true,
61528     /**
61529      * @cfg {Boolean} enableFont Enable font selection. Not available in Safari. (defaults to true)
61530      */
61531     enableFont : true,
61532     /**
61533      * @cfg {String} createLinkText The default text for the create link prompt
61534      */
61535     createLinkText : 'Please enter the URL for the link:',
61536     /**
61537      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
61538      */
61539     defaultLinkValue : 'http:/'+'/',
61540     /**
61541      * @cfg {Array} fontFamilies An array of available font families
61542      */
61543     fontFamilies : [
61544         'Arial',
61545         'Courier New',
61546         'Tahoma',
61547         'Times New Roman',
61548         'Verdana'
61549     ],
61550     defaultFont: 'tahoma',
61551     /**
61552      * @cfg {String} defaultValue A default value to be put into the editor to resolve focus issues (defaults to &#160; (Non-breaking space) in Opera and IE6, &#8203; (Zero-width space) in all other browsers).
61553      */
61554     defaultValue: (Ext.isOpera || Ext.isIE6) ? '&#160;' : '&#8203;',
61555
61556     // private properties
61557     actionMode: 'wrap',
61558     validationEvent : false,
61559     deferHeight: true,
61560     initialized : false,
61561     activated : false,
61562     sourceEditMode : false,
61563     onFocus : Ext.emptyFn,
61564     iframePad:3,
61565     hideMode:'offsets',
61566     defaultAutoCreate : {
61567         tag: "textarea",
61568         style:"width:500px;height:300px;",
61569         autocomplete: "off"
61570     },
61571
61572     // private
61573     initComponent : function(){
61574         this.addEvents(
61575             /**
61576              * @event initialize
61577              * Fires when the editor is fully initialized (including the iframe)
61578              * @param {HtmlEditor} this
61579              */
61580             'initialize',
61581             /**
61582              * @event activate
61583              * Fires when the editor is first receives the focus. Any insertion must wait
61584              * until after this event.
61585              * @param {HtmlEditor} this
61586              */
61587             'activate',
61588              /**
61589              * @event beforesync
61590              * Fires before the textarea is updated with content from the editor iframe. Return false
61591              * to cancel the sync.
61592              * @param {HtmlEditor} this
61593              * @param {String} html
61594              */
61595             'beforesync',
61596              /**
61597              * @event beforepush
61598              * Fires before the iframe editor is updated with content from the textarea. Return false
61599              * to cancel the push.
61600              * @param {HtmlEditor} this
61601              * @param {String} html
61602              */
61603             'beforepush',
61604              /**
61605              * @event sync
61606              * Fires when the textarea is updated with content from the editor iframe.
61607              * @param {HtmlEditor} this
61608              * @param {String} html
61609              */
61610             'sync',
61611              /**
61612              * @event push
61613              * Fires when the iframe editor is updated with content from the textarea.
61614              * @param {HtmlEditor} this
61615              * @param {String} html
61616              */
61617             'push',
61618              /**
61619              * @event editmodechange
61620              * Fires when the editor switches edit modes
61621              * @param {HtmlEditor} this
61622              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
61623              */
61624             'editmodechange'
61625         )
61626     },
61627
61628     // private
61629     createFontOptions : function(){
61630         var buf = [], fs = this.fontFamilies, ff, lc;
61631         for(var i = 0, len = fs.length; i< len; i++){
61632             ff = fs[i];
61633             lc = ff.toLowerCase();
61634             buf.push(
61635                 '<option value="',lc,'" style="font-family:',ff,';"',
61636                     (this.defaultFont == lc ? ' selected="true">' : '>'),
61637                     ff,
61638                 '</option>'
61639             );
61640         }
61641         return buf.join('');
61642     },
61643
61644     /*
61645      * Protected method that will not generally be called directly. It
61646      * is called when the editor creates its toolbar. Override this method if you need to
61647      * add custom toolbar buttons.
61648      * @param {HtmlEditor} editor
61649      */
61650     createToolbar : function(editor){
61651         var items = [];
61652         var tipsEnabled = Ext.QuickTips && Ext.QuickTips.isEnabled();\r
61653
61654
61655         function btn(id, toggle, handler){
61656             return {
61657                 itemId : id,
61658                 cls : 'x-btn-icon',
61659                 iconCls: 'x-edit-'+id,
61660                 enableToggle:toggle !== false,
61661                 scope: editor,
61662                 handler:handler||editor.relayBtnCmd,
61663                 clickEvent:'mousedown',
61664                 tooltip: tipsEnabled ? editor.buttonTips[id] || undefined : undefined,
61665                 overflowText: editor.buttonTips[id].title || undefined,
61666                 tabIndex:-1
61667             };
61668         }
61669
61670
61671         if(this.enableFont && !Ext.isSafari2){\r
61672             var fontSelectItem = new Ext.Toolbar.Item({\r
61673                autoEl: {\r
61674                     tag:'select',\r
61675                     cls:'x-font-select',\r
61676                     html: this.createFontOptions()\r
61677                }\r
61678             });
61679
61680             items.push(
61681                 fontSelectItem,
61682                 '-'
61683             );
61684         }
61685
61686         if(this.enableFormat){
61687             items.push(
61688                 btn('bold'),
61689                 btn('italic'),
61690                 btn('underline')
61691             );
61692         }
61693
61694         if(this.enableFontSize){
61695             items.push(
61696                 '-',
61697                 btn('increasefontsize', false, this.adjustFont),
61698                 btn('decreasefontsize', false, this.adjustFont)
61699             );
61700         }
61701
61702         if(this.enableColors){
61703             items.push(
61704                 '-', {
61705                     itemId:'forecolor',
61706                     cls:'x-btn-icon',
61707                     iconCls: 'x-edit-forecolor',
61708                     clickEvent:'mousedown',
61709                     tooltip: tipsEnabled ? editor.buttonTips.forecolor || undefined : undefined,
61710                     tabIndex:-1,
61711                     menu : new Ext.menu.ColorMenu({
61712                         allowReselect: true,
61713                         focus: Ext.emptyFn,
61714                         value:'000000',
61715                         plain:true,
61716                         listeners: {
61717                             scope: this,
61718                             select: function(cp, color){
61719                                 this.execCmd('forecolor', Ext.isWebKit || Ext.isIE ? '#'+color : color);
61720                                 this.deferFocus();
61721                             }
61722                         },
61723                         clickEvent:'mousedown'
61724                     })
61725                 }, {
61726                     itemId:'backcolor',
61727                     cls:'x-btn-icon',
61728                     iconCls: 'x-edit-backcolor',
61729                     clickEvent:'mousedown',
61730                     tooltip: tipsEnabled ? editor.buttonTips.backcolor || undefined : undefined,
61731                     tabIndex:-1,
61732                     menu : new Ext.menu.ColorMenu({
61733                         focus: Ext.emptyFn,
61734                         value:'FFFFFF',
61735                         plain:true,
61736                         allowReselect: true,
61737                         listeners: {
61738                             scope: this,
61739                             select: function(cp, color){
61740                                 if(Ext.isGecko){
61741                                     this.execCmd('useCSS', false);
61742                                     this.execCmd('hilitecolor', color);
61743                                     this.execCmd('useCSS', true);
61744                                     this.deferFocus();
61745                                 }else{
61746                                     this.execCmd(Ext.isOpera ? 'hilitecolor' : 'backcolor', Ext.isWebKit || Ext.isIE ? '#'+color : color);
61747                                     this.deferFocus();
61748                                 }
61749                             }
61750                         },
61751                         clickEvent:'mousedown'
61752                     })
61753                 }
61754             );
61755         }
61756
61757         if(this.enableAlignments){
61758             items.push(
61759                 '-',
61760                 btn('justifyleft'),
61761                 btn('justifycenter'),
61762                 btn('justifyright')
61763             );
61764         }
61765
61766         if(!Ext.isSafari2){
61767             if(this.enableLinks){
61768                 items.push(
61769                     '-',
61770                     btn('createlink', false, this.createLink)
61771                 );
61772             }
61773
61774             if(this.enableLists){
61775                 items.push(
61776                     '-',
61777                     btn('insertorderedlist'),
61778                     btn('insertunorderedlist')
61779                 );
61780             }
61781             if(this.enableSourceEdit){
61782                 items.push(
61783                     '-',
61784                     btn('sourceedit', true, function(btn){
61785                         this.toggleSourceEdit(!this.sourceEditMode);
61786                     })
61787                 );
61788             }
61789         }\r
61790 \r
61791         // build the toolbar\r
61792         var tb = new Ext.Toolbar({\r
61793             renderTo: this.wrap.dom.firstChild,\r
61794             items: items\r
61795         });\r
61796 \r
61797         if (fontSelectItem) {\r
61798             this.fontSelect = fontSelectItem.el;\r
61799 \r
61800             this.mon(this.fontSelect, 'change', function(){\r
61801                 var font = this.fontSelect.dom.value;\r
61802                 this.relayCmd('fontname', font);\r
61803                 this.deferFocus();\r
61804             }, this);\r
61805         }\r
61806 \r
61807 \r
61808         // stop form submits\r
61809         this.mon(tb.el, 'click', function(e){\r
61810             e.preventDefault();\r
61811         });\r
61812
61813         this.tb = tb;
61814     },
61815
61816     onDisable: function(){
61817         this.wrap.mask();
61818         Ext.form.HtmlEditor.superclass.onDisable.call(this);
61819     },
61820
61821     onEnable: function(){
61822         this.wrap.unmask();
61823         Ext.form.HtmlEditor.superclass.onEnable.call(this);
61824     },
61825
61826     setReadOnly: function(readOnly){
61827
61828         Ext.form.HtmlEditor.superclass.setReadOnly.call(this, readOnly);
61829         if(this.initialized){\r
61830             this.setDesignMode(!readOnly);\r
61831             var bd = this.getEditorBody();\r
61832             if(bd){\r
61833                 bd.style.cursor = this.readOnly ? 'default' : 'text';\r
61834             }\r
61835             this.disableItems(readOnly);\r
61836         }\r
61837     },
61838
61839     /**
61840      * Protected method that will not generally be called directly. It
61841      * is called when the editor initializes the iframe with HTML contents. Override this method if you
61842      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
61843      *\r
61844      * Note: IE8-Standards has unwanted scroller behavior, so the default meta tag forces IE7 compatibility\r
61845      */
61846     getDocMarkup : function(){
61847         return '<html><head><meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7" /><style type="text/css">body{border:0;margin:0;padding:3px;height:98%;cursor:text;}</style></head><body></body></html>';
61848     },
61849
61850     // private
61851     getEditorBody : function(){
61852         var doc = this.getDoc();
61853         return doc.body || doc.documentElement;
61854     },
61855
61856     // private
61857     getDoc : function(){
61858         return Ext.isIE ? this.getWin().document : (this.iframe.contentDocument || this.getWin().document);
61859     },
61860
61861     // private
61862     getWin : function(){
61863         return Ext.isIE ? this.iframe.contentWindow : window.frames[this.iframe.name];
61864     },
61865
61866     // private
61867     onRender : function(ct, position){
61868         Ext.form.HtmlEditor.superclass.onRender.call(this, ct, position);
61869         this.el.dom.style.border = '0 none';
61870         this.el.dom.setAttribute('tabIndex', -1);
61871         this.el.addClass('x-hidden');
61872         if(Ext.isIE){ // fix IE 1px bogus margin
61873             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
61874         }
61875         this.wrap = this.el.wrap({
61876             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
61877         });
61878
61879         this.createToolbar(this);
61880
61881         this.disableItems(true);
61882
61883         this.tb.doLayout();
61884
61885         this.createIFrame();
61886
61887         if(!this.width){
61888             var sz = this.el.getSize();
61889             this.setSize(sz.width, this.height || sz.height);
61890         }
61891         this.resizeEl = this.positionEl = this.wrap;
61892     },
61893
61894     createIFrame: function(){
61895         var iframe = document.createElement('iframe');
61896         iframe.name = Ext.id();
61897         iframe.frameBorder = '0';
61898         iframe.style.overflow = 'auto';\r
61899
61900         this.wrap.dom.appendChild(iframe);
61901         this.iframe = iframe;
61902
61903         this.monitorTask = Ext.TaskMgr.start({
61904             run: this.checkDesignMode,
61905             scope: this,
61906             interval:100
61907         });
61908     },
61909
61910     initFrame : function(){
61911         Ext.TaskMgr.stop(this.monitorTask);
61912         var doc = this.getDoc();
61913         this.win = this.getWin();
61914
61915         doc.open();
61916         doc.write(this.getDocMarkup());
61917         doc.close();
61918
61919         var task = { // must defer to wait for browser to be ready
61920             run : function(){
61921                 var doc = this.getDoc();
61922                 if(doc.body || doc.readyState == 'complete'){
61923                     Ext.TaskMgr.stop(task);
61924                     this.setDesignMode(true);
61925                     this.initEditor.defer(10, this);
61926                 }
61927             },
61928             interval : 10,
61929             duration:10000,
61930             scope: this
61931         };
61932         Ext.TaskMgr.start(task);
61933     },
61934
61935
61936     checkDesignMode : function(){
61937         if(this.wrap && this.wrap.dom.offsetWidth){
61938             var doc = this.getDoc();
61939             if(!doc){
61940                 return;
61941             }
61942             if(!doc.editorInitialized || this.getDesignMode() != 'on'){
61943                 this.initFrame();
61944             }
61945         }
61946     },
61947 \r
61948     /* private\r
61949      * set current design mode. To enable, mode can be true or 'on', off otherwise\r
61950      */\r
61951     setDesignMode : function(mode){\r
61952         var doc ;\r
61953         if(doc = this.getDoc()){\r
61954             if(this.readOnly){\r
61955                 mode = false;\r
61956             }\r
61957             doc.designMode = (/on|true/i).test(String(mode).toLowerCase()) ?'on':'off';\r
61958         }\r
61959 \r
61960     },\r
61961 \r
61962     // private\r
61963     getDesignMode : function(){
61964         var doc = this.getDoc();\r
61965         if(!doc){ return ''; }\r
61966         return String(doc.designMode).toLowerCase();\r
61967 \r
61968     },\r
61969 \r
61970     disableItems: function(disabled){
61971         if(this.fontSelect){
61972             this.fontSelect.dom.disabled = disabled;
61973         }
61974         this.tb.items.each(function(item){
61975             if(item.getItemId() != 'sourceedit'){
61976                 item.setDisabled(disabled);
61977             }
61978         });
61979     },
61980
61981     // private
61982     onResize : function(w, h){
61983         Ext.form.HtmlEditor.superclass.onResize.apply(this, arguments);
61984         if(this.el && this.iframe){
61985             if(Ext.isNumber(w)){
61986                 var aw = w - this.wrap.getFrameWidth('lr');
61987                 this.el.setWidth(aw);
61988                 this.tb.setWidth(aw);
61989                 this.iframe.style.width = Math.max(aw, 0) + 'px';
61990             }
61991             if(Ext.isNumber(h)){
61992                 var ah = h - this.wrap.getFrameWidth('tb') - this.tb.el.getHeight();
61993                 this.el.setHeight(ah);
61994                 this.iframe.style.height = Math.max(ah, 0) + 'px';
61995                 var bd = this.getEditorBody();
61996                 if(bd){
61997                     bd.style.height = Math.max((ah - (this.iframePad*2)), 0) + 'px';
61998                 }
61999             }
62000         }
62001     },
62002
62003     /**
62004      * Toggles the editor between standard and source edit mode.
62005      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
62006      */
62007     toggleSourceEdit : function(sourceEditMode){
62008         if(sourceEditMode === undefined){
62009             sourceEditMode = !this.sourceEditMode;
62010         }
62011         this.sourceEditMode = sourceEditMode === true;
62012         var btn = this.tb.getComponent('sourceedit');
62013
62014         if(btn.pressed !== this.sourceEditMode){
62015             btn.toggle(this.sourceEditMode);
62016             if(!btn.xtbHidden){
62017                 return;
62018             }
62019         }
62020         if(this.sourceEditMode){
62021             this.disableItems(true);
62022             this.syncValue();
62023             this.iframe.className = 'x-hidden';
62024             this.el.removeClass('x-hidden');
62025             this.el.dom.removeAttribute('tabIndex');
62026             this.el.focus();
62027         }else{
62028             if(this.initialized){
62029                 this.disableItems(this.readOnly);
62030             }
62031             this.pushValue();
62032             this.iframe.className = '';
62033             this.el.addClass('x-hidden');
62034             this.el.dom.setAttribute('tabIndex', -1);
62035             this.deferFocus();
62036         }
62037         var lastSize = this.lastSize;
62038         if(lastSize){
62039             delete this.lastSize;
62040             this.setSize(lastSize);
62041         }
62042         this.fireEvent('editmodechange', this, this.sourceEditMode);
62043     },
62044
62045     // private used internally
62046     createLink : function(){
62047         var url = prompt(this.createLinkText, this.defaultLinkValue);
62048         if(url && url != 'http:/'+'/'){
62049             this.relayCmd('createlink', url);
62050         }
62051     },
62052
62053     // private
62054     initEvents : function(){
62055         this.originalValue = this.getValue();
62056     },
62057
62058     /**
62059      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
62060      * @method
62061      */
62062     markInvalid : Ext.emptyFn,
62063
62064     /**
62065      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
62066      * @method
62067      */
62068     clearInvalid : Ext.emptyFn,
62069
62070     // docs inherit from Field
62071     setValue : function(v){
62072         Ext.form.HtmlEditor.superclass.setValue.call(this, v);
62073         this.pushValue();
62074         return this;
62075     },
62076
62077     /**
62078      * Protected method that will not generally be called directly. If you need/want
62079      * custom HTML cleanup, this is the method you should override.
62080      * @param {String} html The HTML to be cleaned
62081      * @return {String} The cleaned HTML
62082      */
62083     cleanHtml: function(html) {
62084         html = String(html);
62085         if(Ext.isWebKit){ // strip safari nonsense
62086             html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
62087         }
62088
62089         /*
62090          * Neat little hack. Strips out all the non-digit characters from the default
62091          * value and compares it to the character code of the first character in the string
62092          * because it can cause encoding issues when posted to the server.
62093          */
62094         if(html.charCodeAt(0) == this.defaultValue.replace(/\D/g, '')){
62095             html = html.substring(1);
62096         }
62097         return html;
62098     },
62099
62100     /**
62101      * Protected method that will not generally be called directly. Syncs the contents
62102      * of the editor iframe with the textarea.
62103      */
62104     syncValue : function(){
62105         if(this.initialized){
62106             var bd = this.getEditorBody();
62107             var html = bd.innerHTML;
62108             if(Ext.isWebKit){
62109                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
62110                 var m = bs.match(/text-align:(.*?);/i);
62111                 if(m && m[1]){
62112                     html = '<div style="'+m[0]+'">' + html + '</div>';
62113                 }
62114             }
62115             html = this.cleanHtml(html);
62116             if(this.fireEvent('beforesync', this, html) !== false){
62117                 this.el.dom.value = html;
62118                 this.fireEvent('sync', this, html);
62119             }
62120         }
62121     },
62122
62123     //docs inherit from Field
62124     getValue : function() {
62125         this[this.sourceEditMode ? 'pushValue' : 'syncValue']();
62126         return Ext.form.HtmlEditor.superclass.getValue.call(this);
62127     },
62128
62129     /**
62130      * Protected method that will not generally be called directly. Pushes the value of the textarea
62131      * into the iframe editor.
62132      */
62133     pushValue : function(){
62134         if(this.initialized){
62135             var v = this.el.dom.value;
62136             if(!this.activated && v.length < 1){
62137                 v = this.defaultValue;
62138             }
62139             if(this.fireEvent('beforepush', this, v) !== false){
62140                 this.getEditorBody().innerHTML = v;
62141                 if(Ext.isGecko){
62142                     // Gecko hack, see: https://bugzilla.mozilla.org/show_bug.cgi?id=232791#c8
62143                     this.setDesignMode(false);  //toggle off first\r
62144 \r
62145                 }
62146                 this.setDesignMode(true);\r
62147                 this.fireEvent('push', this, v);
62148             }
62149 \r
62150         }
62151     },
62152
62153     // private
62154     deferFocus : function(){
62155         this.focus.defer(10, this);
62156     },
62157
62158     // docs inherit from Field
62159     focus : function(){
62160         if(this.win && !this.sourceEditMode){
62161             this.win.focus();
62162         }else{
62163             this.el.focus();
62164         }
62165     },
62166
62167     // private
62168     initEditor : function(){
62169         //Destroying the component during/before initEditor can cause issues.
62170         try{
62171             var dbody = this.getEditorBody(),
62172                 ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat'),
62173                 doc,
62174                 fn;
62175
62176             ss['background-attachment'] = 'fixed'; // w3c
62177             dbody.bgProperties = 'fixed'; // ie
62178
62179             Ext.DomHelper.applyStyles(dbody, ss);
62180
62181             doc = this.getDoc();
62182
62183             if(doc){
62184                 try{
62185                     Ext.EventManager.removeAll(doc);
62186                 }catch(e){}
62187             }
62188
62189             /*
62190              * We need to use createDelegate here, because when using buffer, the delayed task is added
62191              * as a property to the function. When the listener is removed, the task is deleted from the function.
62192              * Since onEditorEvent is shared on the prototype, if we have multiple html editors, the first time one of the editors
62193              * is destroyed, it causes the fn to be deleted from the prototype, which causes errors. Essentially, we're just anonymizing the function.
62194              */
62195             fn = this.onEditorEvent.createDelegate(this);
62196             Ext.EventManager.on(doc, {
62197                 mousedown: fn,
62198                 dblclick: fn,
62199                 click: fn,
62200                 keyup: fn,
62201                 buffer:100
62202             });
62203
62204             if(Ext.isGecko){
62205                 Ext.EventManager.on(doc, 'keypress', this.applyCommand, this);
62206             }
62207             if(Ext.isIE || Ext.isWebKit || Ext.isOpera){
62208                 Ext.EventManager.on(doc, 'keydown', this.fixKeys, this);
62209             }
62210             doc.editorInitialized = true;
62211             this.initialized = true;
62212             this.pushValue();
62213             this.setReadOnly(this.readOnly);
62214             this.fireEvent('initialize', this);
62215         }catch(e){}
62216     },
62217
62218     // private
62219     onDestroy : function(){
62220         if(this.monitorTask){
62221             Ext.TaskMgr.stop(this.monitorTask);
62222         }
62223         if(this.rendered){
62224             Ext.destroy(this.tb);
62225             var doc = this.getDoc();
62226             if(doc){
62227                 try{
62228                     Ext.EventManager.removeAll(doc);
62229                     for (var prop in doc){
62230                         delete doc[prop];
62231                     }
62232                 }catch(e){}
62233             }
62234             if(this.wrap){
62235                 this.wrap.dom.innerHTML = '';
62236                 this.wrap.remove();
62237             }
62238         }
62239
62240         if(this.el){
62241             this.el.removeAllListeners();
62242             this.el.remove();
62243         }
62244         this.purgeListeners();
62245     },
62246
62247     // private
62248     onFirstFocus : function(){
62249         this.activated = true;
62250         this.disableItems(this.readOnly);
62251         if(Ext.isGecko){ // prevent silly gecko errors
62252             this.win.focus();
62253             var s = this.win.getSelection();
62254             if(!s.focusNode || s.focusNode.nodeType != 3){
62255                 var r = s.getRangeAt(0);
62256                 r.selectNodeContents(this.getEditorBody());
62257                 r.collapse(true);
62258                 this.deferFocus();
62259             }
62260             try{
62261                 this.execCmd('useCSS', true);
62262                 this.execCmd('styleWithCSS', false);
62263             }catch(e){}
62264         }
62265         this.fireEvent('activate', this);
62266     },
62267
62268     // private
62269     adjustFont: function(btn){
62270         var adjust = btn.getItemId() == 'increasefontsize' ? 1 : -1,
62271             doc = this.getDoc(),
62272             v = parseInt(doc.queryCommandValue('FontSize') || 2, 10);
62273         if((Ext.isSafari && !Ext.isSafari2) || Ext.isChrome || Ext.isAir){
62274             // Safari 3 values
62275             // 1 = 10px, 2 = 13px, 3 = 16px, 4 = 18px, 5 = 24px, 6 = 32px
62276             if(v <= 10){
62277                 v = 1 + adjust;
62278             }else if(v <= 13){
62279                 v = 2 + adjust;
62280             }else if(v <= 16){
62281                 v = 3 + adjust;
62282             }else if(v <= 18){
62283                 v = 4 + adjust;
62284             }else if(v <= 24){
62285                 v = 5 + adjust;
62286             }else {
62287                 v = 6 + adjust;
62288             }
62289             v = v.constrain(1, 6);
62290         }else{
62291             if(Ext.isSafari){ // safari
62292                 adjust *= 2;
62293             }
62294             v = Math.max(1, v+adjust) + (Ext.isSafari ? 'px' : 0);
62295         }
62296         this.execCmd('FontSize', v);
62297     },
62298
62299     // private
62300     onEditorEvent : function(e){
62301         this.updateToolbar();
62302     },
62303
62304
62305     /**
62306      * Protected method that will not generally be called directly. It triggers
62307      * a toolbar update by reading the markup state of the current selection in the editor.
62308      */
62309     updateToolbar: function(){
62310
62311         if(this.readOnly){
62312             return;
62313         }
62314
62315         if(!this.activated){
62316             this.onFirstFocus();
62317             return;
62318         }
62319
62320         var btns = this.tb.items.map,
62321             doc = this.getDoc();
62322
62323         if(this.enableFont && !Ext.isSafari2){
62324             var name = (doc.queryCommandValue('FontName')||this.defaultFont).toLowerCase();
62325             if(name != this.fontSelect.dom.value){
62326                 this.fontSelect.dom.value = name;
62327             }
62328         }
62329         if(this.enableFormat){
62330             btns.bold.toggle(doc.queryCommandState('bold'));
62331             btns.italic.toggle(doc.queryCommandState('italic'));
62332             btns.underline.toggle(doc.queryCommandState('underline'));
62333         }
62334         if(this.enableAlignments){
62335             btns.justifyleft.toggle(doc.queryCommandState('justifyleft'));
62336             btns.justifycenter.toggle(doc.queryCommandState('justifycenter'));
62337             btns.justifyright.toggle(doc.queryCommandState('justifyright'));
62338         }
62339         if(!Ext.isSafari2 && this.enableLists){
62340             btns.insertorderedlist.toggle(doc.queryCommandState('insertorderedlist'));
62341             btns.insertunorderedlist.toggle(doc.queryCommandState('insertunorderedlist'));
62342         }
62343
62344         Ext.menu.MenuMgr.hideAll();
62345
62346         this.syncValue();
62347     },
62348
62349     // private
62350     relayBtnCmd : function(btn){
62351         this.relayCmd(btn.getItemId());
62352     },
62353
62354     /**
62355      * Executes a Midas editor command on the editor document and performs necessary focus and
62356      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
62357      * @param {String} cmd The Midas command
62358      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
62359      */
62360     relayCmd : function(cmd, value){
62361         (function(){
62362             this.focus();
62363             this.execCmd(cmd, value);
62364             this.updateToolbar();
62365         }).defer(10, this);
62366     },
62367
62368     /**
62369      * Executes a Midas editor command directly on the editor document.
62370      * For visual commands, you should use {@link #relayCmd} instead.
62371      * <b>This should only be called after the editor is initialized.</b>
62372      * @param {String} cmd The Midas command
62373      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
62374      */
62375     execCmd : function(cmd, value){
62376         var doc = this.getDoc();
62377         doc.execCommand(cmd, false, value === undefined ? null : value);
62378         this.syncValue();
62379     },
62380
62381     // private
62382     applyCommand : function(e){
62383         if(e.ctrlKey){
62384             var c = e.getCharCode(), cmd;
62385             if(c > 0){
62386                 c = String.fromCharCode(c);
62387                 switch(c){
62388                     case 'b':
62389                         cmd = 'bold';
62390                     break;
62391                     case 'i':
62392                         cmd = 'italic';
62393                     break;
62394                     case 'u':
62395                         cmd = 'underline';
62396                     break;
62397                 }
62398                 if(cmd){
62399                     this.win.focus();
62400                     this.execCmd(cmd);
62401                     this.deferFocus();
62402                     e.preventDefault();
62403                 }
62404             }
62405         }
62406     },
62407
62408     /**
62409      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
62410      * to insert text.
62411      * @param {String} text
62412      */
62413     insertAtCursor : function(text){
62414         if(!this.activated){
62415             return;
62416         }
62417         if(Ext.isIE){
62418             this.win.focus();
62419             var doc = this.getDoc(),
62420                 r = doc.selection.createRange();
62421             if(r){
62422                 r.pasteHTML(text);
62423                 this.syncValue();
62424                 this.deferFocus();
62425             }
62426         }else{
62427             this.win.focus();
62428             this.execCmd('InsertHTML', text);
62429             this.deferFocus();
62430         }
62431     },
62432
62433     // private
62434     fixKeys : function(){ // load time branching for fastest keydown performance
62435         if(Ext.isIE){
62436             return function(e){
62437                 var k = e.getKey(),
62438                     doc = this.getDoc(),
62439                         r;
62440                 if(k == e.TAB){
62441                     e.stopEvent();
62442                     r = doc.selection.createRange();
62443                     if(r){
62444                         r.collapse(true);
62445                         r.pasteHTML('&nbsp;&nbsp;&nbsp;&nbsp;');
62446                         this.deferFocus();
62447                     }
62448                 }else if(k == e.ENTER){
62449                     r = doc.selection.createRange();
62450                     if(r){
62451                         var target = r.parentElement();
62452                         if(!target || target.tagName.toLowerCase() != 'li'){
62453                             e.stopEvent();
62454                             r.pasteHTML('<br />');
62455                             r.collapse(false);
62456                             r.select();
62457                         }
62458                     }
62459                 }
62460             };
62461         }else if(Ext.isOpera){
62462             return function(e){
62463                 var k = e.getKey();
62464                 if(k == e.TAB){
62465                     e.stopEvent();
62466                     this.win.focus();
62467                     this.execCmd('InsertHTML','&nbsp;&nbsp;&nbsp;&nbsp;');
62468                     this.deferFocus();
62469                 }
62470             };
62471         }else if(Ext.isWebKit){
62472             return function(e){
62473                 var k = e.getKey();
62474                 if(k == e.TAB){
62475                     e.stopEvent();
62476                     this.execCmd('InsertText','\t');
62477                     this.deferFocus();
62478                 }else if(k == e.ENTER){
62479                     e.stopEvent();
62480                     this.execCmd('InsertHtml','<br /><br />');
62481                     this.deferFocus();
62482                 }
62483              };
62484         }
62485     }(),
62486
62487     /**
62488      * Returns the editor's toolbar. <b>This is only available after the editor has been rendered.</b>
62489      * @return {Ext.Toolbar}
62490      */
62491     getToolbar : function(){
62492         return this.tb;
62493     },
62494
62495     /**
62496      * Object collection of toolbar tooltips for the buttons in the editor. The key
62497      * is the command id associated with that button and the value is a valid QuickTips object.
62498      * For example:
62499 <pre><code>
62500 {
62501     bold : {
62502         title: 'Bold (Ctrl+B)',
62503         text: 'Make the selected text bold.',
62504         cls: 'x-html-editor-tip'
62505     },
62506     italic : {
62507         title: 'Italic (Ctrl+I)',
62508         text: 'Make the selected text italic.',
62509         cls: 'x-html-editor-tip'
62510     },
62511     ...
62512 </code></pre>
62513     * @type Object
62514      */
62515     buttonTips : {
62516         bold : {
62517             title: 'Bold (Ctrl+B)',
62518             text: 'Make the selected text bold.',
62519             cls: 'x-html-editor-tip'
62520         },
62521         italic : {
62522             title: 'Italic (Ctrl+I)',
62523             text: 'Make the selected text italic.',
62524             cls: 'x-html-editor-tip'
62525         },
62526         underline : {
62527             title: 'Underline (Ctrl+U)',
62528             text: 'Underline the selected text.',
62529             cls: 'x-html-editor-tip'
62530         },
62531         increasefontsize : {
62532             title: 'Grow Text',
62533             text: 'Increase the font size.',
62534             cls: 'x-html-editor-tip'
62535         },
62536         decreasefontsize : {
62537             title: 'Shrink Text',
62538             text: 'Decrease the font size.',
62539             cls: 'x-html-editor-tip'
62540         },
62541         backcolor : {
62542             title: 'Text Highlight Color',
62543             text: 'Change the background color of the selected text.',
62544             cls: 'x-html-editor-tip'
62545         },
62546         forecolor : {
62547             title: 'Font Color',
62548             text: 'Change the color of the selected text.',
62549             cls: 'x-html-editor-tip'
62550         },
62551         justifyleft : {
62552             title: 'Align Text Left',
62553             text: 'Align text to the left.',
62554             cls: 'x-html-editor-tip'
62555         },
62556         justifycenter : {
62557             title: 'Center Text',
62558             text: 'Center text in the editor.',
62559             cls: 'x-html-editor-tip'
62560         },
62561         justifyright : {
62562             title: 'Align Text Right',
62563             text: 'Align text to the right.',
62564             cls: 'x-html-editor-tip'
62565         },
62566         insertunorderedlist : {
62567             title: 'Bullet List',
62568             text: 'Start a bulleted list.',
62569             cls: 'x-html-editor-tip'
62570         },
62571         insertorderedlist : {
62572             title: 'Numbered List',
62573             text: 'Start a numbered list.',
62574             cls: 'x-html-editor-tip'
62575         },
62576         createlink : {
62577             title: 'Hyperlink',
62578             text: 'Make the selected text a hyperlink.',
62579             cls: 'x-html-editor-tip'
62580         },
62581         sourceedit : {
62582             title: 'Source Edit',
62583             text: 'Switch to source editing mode.',
62584             cls: 'x-html-editor-tip'
62585         }
62586     }
62587
62588     // hide stuff that is not compatible
62589     /**
62590      * @event blur
62591      * @hide
62592      */
62593     /**
62594      * @event change
62595      * @hide
62596      */
62597     /**
62598      * @event focus
62599      * @hide
62600      */
62601     /**
62602      * @event specialkey
62603      * @hide
62604      */
62605     /**
62606      * @cfg {String} fieldClass @hide
62607      */
62608     /**
62609      * @cfg {String} focusClass @hide
62610      */
62611     /**
62612      * @cfg {String} autoCreate @hide
62613      */
62614     /**
62615      * @cfg {String} inputType @hide
62616      */
62617     /**
62618      * @cfg {String} invalidClass @hide
62619      */
62620     /**
62621      * @cfg {String} invalidText @hide
62622      */
62623     /**
62624      * @cfg {String} msgFx @hide
62625      */
62626     /**
62627      * @cfg {String} validateOnBlur @hide
62628      */
62629     /**
62630      * @cfg {Boolean} allowDomMove  @hide
62631      */
62632     /**
62633      * @cfg {String} applyTo @hide
62634      */
62635     /**
62636      * @cfg {String} autoHeight  @hide
62637      */
62638     /**
62639      * @cfg {String} autoWidth  @hide
62640      */
62641     /**
62642      * @cfg {String} cls  @hide
62643      */
62644     /**
62645      * @cfg {String} disabled  @hide
62646      */
62647     /**
62648      * @cfg {String} disabledClass  @hide
62649      */
62650     /**
62651      * @cfg {String} msgTarget  @hide
62652      */
62653     /**
62654      * @cfg {String} readOnly  @hide
62655      */
62656     /**
62657      * @cfg {String} style  @hide
62658      */
62659     /**
62660      * @cfg {String} validationDelay  @hide
62661      */
62662     /**
62663      * @cfg {String} validationEvent  @hide
62664      */
62665     /**
62666      * @cfg {String} tabIndex  @hide
62667      */
62668     /**
62669      * @property disabled
62670      * @hide
62671      */
62672     /**
62673      * @method applyToMarkup
62674      * @hide
62675      */
62676     /**
62677      * @method disable
62678      * @hide
62679      */
62680     /**
62681      * @method enable
62682      * @hide
62683      */
62684     /**
62685      * @method validate
62686      * @hide
62687      */
62688     /**
62689      * @event valid
62690      * @hide
62691      */
62692     /**
62693      * @method setDisabled
62694      * @hide
62695      */
62696     /**
62697      * @cfg keys
62698      * @hide
62699      */
62700 });
62701 Ext.reg('htmleditor', Ext.form.HtmlEditor);/**\r
62702  * @class Ext.form.TimeField\r
62703  * @extends Ext.form.ComboBox\r
62704  * Provides a time input field with a time dropdown and automatic time validation.  Example usage:\r
62705  * <pre><code>\r
62706 new Ext.form.TimeField({\r
62707     minValue: '9:00 AM',\r
62708     maxValue: '6:00 PM',\r
62709     increment: 30\r
62710 });\r
62711 </code></pre>\r
62712  * @constructor\r
62713  * Create a new TimeField\r
62714  * @param {Object} config\r
62715  * @xtype timefield\r
62716  */\r
62717 Ext.form.TimeField = Ext.extend(Ext.form.ComboBox, {\r
62718     /**\r
62719      * @cfg {Date/String} minValue\r
62720      * The minimum allowed time. Can be either a Javascript date object with a valid time value or a string \r
62721      * time in a valid format -- see {@link #format} and {@link #altFormats} (defaults to undefined).\r
62722      */\r
62723     minValue : undefined,\r
62724     /**\r
62725      * @cfg {Date/String} maxValue\r
62726      * The maximum allowed time. Can be either a Javascript date object with a valid time value or a string \r
62727      * time in a valid format -- see {@link #format} and {@link #altFormats} (defaults to undefined).\r
62728      */\r
62729     maxValue : undefined,\r
62730     /**\r
62731      * @cfg {String} minText\r
62732      * The error text to display when the date in the cell is before minValue (defaults to\r
62733      * 'The time in this field must be equal to or after {0}').\r
62734      */\r
62735     minText : "The time in this field must be equal to or after {0}",\r
62736     /**\r
62737      * @cfg {String} maxText\r
62738      * The error text to display when the time is after maxValue (defaults to\r
62739      * 'The time in this field must be equal to or before {0}').\r
62740      */\r
62741     maxText : "The time in this field must be equal to or before {0}",\r
62742     /**\r
62743      * @cfg {String} invalidText\r
62744      * The error text to display when the time in the field is invalid (defaults to\r
62745      * '{value} is not a valid time').\r
62746      */\r
62747     invalidText : "{0} is not a valid time",\r
62748     /**\r
62749      * @cfg {String} format\r
62750      * The default time format string which can be overriden for localization support.  The format must be\r
62751      * valid according to {@link Date#parseDate} (defaults to 'g:i A', e.g., '3:15 PM').  For 24-hour time\r
62752      * format try 'H:i' instead.\r
62753      */\r
62754     format : "g:i A",\r
62755     /**\r
62756      * @cfg {String} altFormats\r
62757      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined\r
62758      * format (defaults to 'g:ia|g:iA|g:i a|g:i A|h:i|g:i|H:i|ga|ha|gA|h a|g a|g A|gi|hi|gia|hia|g|H').\r
62759      */\r
62760     altFormats : "g:ia|g:iA|g:i a|g:i A|h:i|g:i|H:i|ga|ha|gA|h a|g a|g A|gi|hi|gia|hia|g|H",\r
62761     /**\r
62762      * @cfg {Number} increment\r
62763      * The number of minutes between each time value in the list (defaults to 15).\r
62764      */\r
62765     increment: 15,\r
62766 \r
62767     // private override\r
62768     mode: 'local',\r
62769     // private override\r
62770     triggerAction: 'all',\r
62771     // private override\r
62772     typeAhead: false,\r
62773     \r
62774     // private - This is the date to use when generating time values in the absence of either minValue\r
62775     // or maxValue.  Using the current date causes DST issues on DST boundary dates, so this is an \r
62776     // arbitrary "safe" date that can be any date aside from DST boundary dates.\r
62777     initDate: '1/1/2008',\r
62778 \r
62779     // private\r
62780     initComponent : function(){\r
62781         if(Ext.isDefined(this.minValue)){\r
62782             this.setMinValue(this.minValue, true);\r
62783         }\r
62784         if(Ext.isDefined(this.maxValue)){\r
62785             this.setMaxValue(this.maxValue, true);\r
62786         }\r
62787         if(!this.store){\r
62788             this.generateStore(true);\r
62789         }\r
62790         Ext.form.TimeField.superclass.initComponent.call(this);\r
62791     },\r
62792     \r
62793     /**\r
62794      * Replaces any existing {@link #minValue} with the new time and refreshes the store.\r
62795      * @param {Date/String} value The minimum time that can be selected\r
62796      */\r
62797     setMinValue: function(value, /* private */ initial){\r
62798         this.setLimit(value, true, initial);\r
62799         return this;\r
62800     },\r
62801 \r
62802     /**\r
62803      * Replaces any existing {@link #maxValue} with the new time and refreshes the store.\r
62804      * @param {Date/String} value The maximum time that can be selected\r
62805      */\r
62806     setMaxValue: function(value, /* private */ initial){\r
62807         this.setLimit(value, false, initial);\r
62808         return this;\r
62809     },\r
62810     \r
62811     // private\r
62812     generateStore: function(initial){\r
62813         var min = this.minValue || new Date(this.initDate).clearTime(),\r
62814             max = this.maxValue || new Date(this.initDate).clearTime().add('mi', (24 * 60) - 1),\r
62815             times = [];\r
62816             \r
62817         while(min <= max){\r
62818             times.push(min.dateFormat(this.format));\r
62819             min = min.add('mi', this.increment);\r
62820         }\r
62821         this.bindStore(times, initial);\r
62822     },\r
62823 \r
62824     // private\r
62825     setLimit: function(value, isMin, initial){\r
62826         var d;\r
62827         if(Ext.isString(value)){\r
62828             d = this.parseDate(value);\r
62829         }else if(Ext.isDate(value)){\r
62830             d = value;\r
62831         }\r
62832         if(d){\r
62833             var val = new Date(this.initDate).clearTime();\r
62834             val.setHours(d.getHours(), d.getMinutes(), isMin ? 0 : 59, 0);\r
62835             this[isMin ? 'minValue' : 'maxValue'] = val;\r
62836             if(!initial){\r
62837                 this.generateStore();\r
62838             }\r
62839         }\r
62840     },\r
62841     \r
62842     // inherited docs\r
62843     getValue : function(){\r
62844         var v = Ext.form.TimeField.superclass.getValue.call(this);\r
62845         return this.formatDate(this.parseDate(v)) || '';\r
62846     },\r
62847 \r
62848     // inherited docs\r
62849     setValue : function(value){\r
62850         return Ext.form.TimeField.superclass.setValue.call(this, this.formatDate(this.parseDate(value)));\r
62851     },\r
62852 \r
62853     // private overrides\r
62854     validateValue : Ext.form.DateField.prototype.validateValue,\r
62855     parseDate : Ext.form.DateField.prototype.parseDate,\r
62856     formatDate : Ext.form.DateField.prototype.formatDate,\r
62857 \r
62858     // private\r
62859     beforeBlur : function(){\r
62860         var v = this.parseDate(this.getRawValue());\r
62861         if(v){\r
62862             this.setValue(v.dateFormat(this.format));\r
62863         }\r
62864         Ext.form.TimeField.superclass.beforeBlur.call(this);\r
62865     }\r
62866 \r
62867     /**\r
62868      * @cfg {Boolean} grow @hide\r
62869      */\r
62870     /**\r
62871      * @cfg {Number} growMin @hide\r
62872      */\r
62873     /**\r
62874      * @cfg {Number} growMax @hide\r
62875      */\r
62876     /**\r
62877      * @hide\r
62878      * @method autoSize\r
62879      */\r
62880 });\r
62881 Ext.reg('timefield', Ext.form.TimeField);/**
62882  * @class Ext.form.Label
62883  * @extends Ext.BoxComponent
62884  * Basic Label field.
62885  * @constructor
62886  * Creates a new Label
62887  * @param {Ext.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
62888  * element and its id used as the component id.  If a string is passed, it is assumed to be the id of an existing element
62889  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
62890  * @xtype label
62891  */
62892 Ext.form.Label = Ext.extend(Ext.BoxComponent, {
62893     /**
62894      * @cfg {String} text The plain text to display within the label (defaults to ''). If you need to include HTML
62895      * tags within the label's innerHTML, use the {@link #html} config instead.
62896      */
62897     /**
62898      * @cfg {String} forId The id of the input element to which this label will be bound via the standard HTML 'for'
62899      * attribute. If not specified, the attribute will not be added to the label.
62900      */
62901     /**
62902      * @cfg {String} html An HTML fragment that will be used as the label's innerHTML (defaults to '').
62903      * Note that if {@link #text} is specified it will take precedence and this value will be ignored.
62904      */
62905
62906     // private
62907     onRender : function(ct, position){
62908         if(!this.el){
62909             this.el = document.createElement('label');
62910             this.el.id = this.getId();
62911             this.el.innerHTML = this.text ? Ext.util.Format.htmlEncode(this.text) : (this.html || '');
62912             if(this.forId){
62913                 this.el.setAttribute('for', this.forId);
62914             }
62915         }
62916         Ext.form.Label.superclass.onRender.call(this, ct, position);
62917     },
62918
62919     /**
62920      * Updates the label's innerHTML with the specified string.
62921      * @param {String} text The new label text
62922      * @param {Boolean} encode (optional) False to skip HTML-encoding the text when rendering it
62923      * to the label (defaults to true which encodes the value). This might be useful if you want to include
62924      * tags in the label's innerHTML rather than rendering them as string literals per the default logic.
62925      * @return {Label} this
62926      */
62927     setText : function(t, encode){
62928         var e = encode === false;
62929         this[!e ? 'text' : 'html'] = t;
62930         delete this[e ? 'text' : 'html'];
62931         if(this.rendered){
62932             this.el.dom.innerHTML = encode !== false ? Ext.util.Format.htmlEncode(t) : t;
62933         }
62934         return this;
62935     }
62936 });
62937
62938 Ext.reg('label', Ext.form.Label);/**
62939  * @class Ext.form.Action
62940  * <p>The subclasses of this class provide actions to perform upon {@link Ext.form.BasicForm Form}s.</p>
62941  * <p>Instances of this class are only created by a {@link Ext.form.BasicForm Form} when
62942  * the Form needs to perform an action such as submit or load. The Configuration options
62943  * listed for this class are set through the Form's action methods: {@link Ext.form.BasicForm#submit submit},
62944  * {@link Ext.form.BasicForm#load load} and {@link Ext.form.BasicForm#doAction doAction}</p>
62945  * <p>The instance of Action which performed the action is passed to the success
62946  * and failure callbacks of the Form's action methods ({@link Ext.form.BasicForm#submit submit},
62947  * {@link Ext.form.BasicForm#load load} and {@link Ext.form.BasicForm#doAction doAction}),
62948  * and to the {@link Ext.form.BasicForm#actioncomplete actioncomplete} and
62949  * {@link Ext.form.BasicForm#actionfailed actionfailed} event handlers.</p>
62950  */
62951 Ext.form.Action = function(form, options){
62952     this.form = form;
62953     this.options = options || {};
62954 };
62955
62956 /**
62957  * Failure type returned when client side validation of the Form fails
62958  * thus aborting a submit action. Client side validation is performed unless
62959  * {@link #clientValidation} is explicitly set to <tt>false</tt>.
62960  * @type {String}
62961  * @static
62962  */
62963 Ext.form.Action.CLIENT_INVALID = 'client';
62964 /**
62965  * <p>Failure type returned when server side processing fails and the {@link #result}'s
62966  * <tt style="font-weight:bold">success</tt> property is set to <tt>false</tt>.</p>
62967  * <p>In the case of a form submission, field-specific error messages may be returned in the
62968  * {@link #result}'s <tt style="font-weight:bold">errors</tt> property.</p>
62969  * @type {String}
62970  * @static
62971  */
62972 Ext.form.Action.SERVER_INVALID = 'server';
62973 /**
62974  * Failure type returned when a communication error happens when attempting
62975  * to send a request to the remote server. The {@link #response} may be examined to
62976  * provide further information.
62977  * @type {String}
62978  * @static
62979  */
62980 Ext.form.Action.CONNECT_FAILURE = 'connect';
62981 /**
62982  * Failure type returned when the response's <tt style="font-weight:bold">success</tt>
62983  * property is set to <tt>false</tt>, or no field values are returned in the response's
62984  * <tt style="font-weight:bold">data</tt> property.
62985  * @type {String}
62986  * @static
62987  */
62988 Ext.form.Action.LOAD_FAILURE = 'load';
62989
62990 Ext.form.Action.prototype = {
62991 /**
62992  * @cfg {String} url The URL that the Action is to invoke.
62993  */
62994 /**
62995  * @cfg {Boolean} reset When set to <tt><b>true</b></tt>, causes the Form to be
62996  * {@link Ext.form.BasicForm.reset reset} on Action success. If specified, this happens
62997  * <b>before</b> the {@link #success} callback is called and before the Form's
62998  * {@link Ext.form.BasicForm.actioncomplete actioncomplete} event fires.
62999  */
63000 /**
63001  * @cfg {String} method The HTTP method to use to access the requested URL. Defaults to the
63002  * {@link Ext.form.BasicForm}'s method, or if that is not specified, the underlying DOM form's method.
63003  */
63004 /**
63005  * @cfg {Mixed} params <p>Extra parameter values to pass. These are added to the Form's
63006  * {@link Ext.form.BasicForm#baseParams} and passed to the specified URL along with the Form's
63007  * input fields.</p>
63008  * <p>Parameters are encoded as standard HTTP parameters using {@link Ext#urlEncode}.</p>
63009  */
63010 /**
63011  * @cfg {Number} timeout The number of seconds to wait for a server response before
63012  * failing with the {@link #failureType} as {@link #Action.CONNECT_FAILURE}. If not specified,
63013  * defaults to the configured <tt>{@link Ext.form.BasicForm#timeout timeout}</tt> of the
63014  * {@link Ext.form.BasicForm form}.
63015  */
63016 /**
63017  * @cfg {Function} success The function to call when a valid success return packet is recieved.
63018  * The function is passed the following parameters:<ul class="mdetail-params">
63019  * <li><b>form</b> : Ext.form.BasicForm<div class="sub-desc">The form that requested the action</div></li>
63020  * <li><b>action</b> : Ext.form.Action<div class="sub-desc">The Action class. The {@link #result}
63021  * property of this object may be examined to perform custom postprocessing.</div></li>
63022  * </ul>
63023  */
63024 /**
63025  * @cfg {Function} failure The function to call when a failure packet was recieved, or when an
63026  * error ocurred in the Ajax communication.
63027  * The function is passed the following parameters:<ul class="mdetail-params">
63028  * <li><b>form</b> : Ext.form.BasicForm<div class="sub-desc">The form that requested the action</div></li>
63029  * <li><b>action</b> : Ext.form.Action<div class="sub-desc">The Action class. If an Ajax
63030  * error ocurred, the failure type will be in {@link #failureType}. The {@link #result}
63031  * property of this object may be examined to perform custom postprocessing.</div></li>
63032  * </ul>
63033  */
63034 /**
63035  * @cfg {Object} scope The scope in which to call the callback functions (The <tt>this</tt> reference
63036  * for the callback functions).
63037  */
63038 /**
63039  * @cfg {String} waitMsg The message to be displayed by a call to {@link Ext.MessageBox#wait}
63040  * during the time the action is being processed.
63041  */
63042 /**
63043  * @cfg {String} waitTitle The title to be displayed by a call to {@link Ext.MessageBox#wait}
63044  * during the time the action is being processed.
63045  */
63046
63047 /**
63048  * The type of action this Action instance performs.
63049  * Currently only "submit" and "load" are supported.
63050  * @type {String}
63051  */
63052     type : 'default',
63053 /**
63054  * The type of failure detected will be one of these: {@link #CLIENT_INVALID},
63055  * {@link #SERVER_INVALID}, {@link #CONNECT_FAILURE}, or {@link #LOAD_FAILURE}.  Usage:
63056  * <pre><code>
63057 var fp = new Ext.form.FormPanel({
63058 ...
63059 buttons: [{
63060     text: 'Save',
63061     formBind: true,
63062     handler: function(){
63063         if(fp.getForm().isValid()){
63064             fp.getForm().submit({
63065                 url: 'form-submit.php',
63066                 waitMsg: 'Submitting your data...',
63067                 success: function(form, action){
63068                     // server responded with success = true
63069                     var result = action.{@link #result};
63070                 },
63071                 failure: function(form, action){
63072                     if (action.{@link #failureType} === Ext.form.Action.{@link #CONNECT_FAILURE}) {
63073                         Ext.Msg.alert('Error',
63074                             'Status:'+action.{@link #response}.status+': '+
63075                             action.{@link #response}.statusText);
63076                     }
63077                     if (action.failureType === Ext.form.Action.{@link #SERVER_INVALID}){
63078                         // server responded with success = false
63079                         Ext.Msg.alert('Invalid', action.{@link #result}.errormsg);
63080                     }
63081                 }
63082             });
63083         }
63084     }
63085 },{
63086     text: 'Reset',
63087     handler: function(){
63088         fp.getForm().reset();
63089     }
63090 }]
63091  * </code></pre>
63092  * @property failureType
63093  * @type {String}
63094  */
63095  /**
63096  * The XMLHttpRequest object used to perform the action.
63097  * @property response
63098  * @type {Object}
63099  */
63100  /**
63101  * The decoded response object containing a boolean <tt style="font-weight:bold">success</tt> property and
63102  * other, action-specific properties.
63103  * @property result
63104  * @type {Object}
63105  */
63106
63107     // interface method
63108     run : function(options){
63109
63110     },
63111
63112     // interface method
63113     success : function(response){
63114
63115     },
63116
63117     // interface method
63118     handleResponse : function(response){
63119
63120     },
63121
63122     // default connection failure
63123     failure : function(response){
63124         this.response = response;
63125         this.failureType = Ext.form.Action.CONNECT_FAILURE;
63126         this.form.afterAction(this, false);
63127     },
63128
63129     // private
63130     // shared code among all Actions to validate that there was a response
63131     // with either responseText or responseXml
63132     processResponse : function(response){
63133         this.response = response;
63134         if(!response.responseText && !response.responseXML){
63135             return true;
63136         }
63137         this.result = this.handleResponse(response);
63138         return this.result;
63139     },
63140
63141     // utility functions used internally
63142     getUrl : function(appendParams){
63143         var url = this.options.url || this.form.url || this.form.el.dom.action;
63144         if(appendParams){
63145             var p = this.getParams();
63146             if(p){
63147                 url = Ext.urlAppend(url, p);
63148             }
63149         }
63150         return url;
63151     },
63152
63153     // private
63154     getMethod : function(){
63155         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
63156     },
63157
63158     // private
63159     getParams : function(){
63160         var bp = this.form.baseParams;
63161         var p = this.options.params;
63162         if(p){
63163             if(typeof p == "object"){
63164                 p = Ext.urlEncode(Ext.applyIf(p, bp));
63165             }else if(typeof p == 'string' && bp){
63166                 p += '&' + Ext.urlEncode(bp);
63167             }
63168         }else if(bp){
63169             p = Ext.urlEncode(bp);
63170         }
63171         return p;
63172     },
63173
63174     // private
63175     createCallback : function(opts){
63176         var opts = opts || {};
63177         return {
63178             success: this.success,
63179             failure: this.failure,
63180             scope: this,
63181             timeout: (opts.timeout*1000) || (this.form.timeout*1000),
63182             upload: this.form.fileUpload ? this.success : undefined
63183         };
63184     }
63185 };
63186
63187 /**
63188  * @class Ext.form.Action.Submit
63189  * @extends Ext.form.Action
63190  * <p>A class which handles submission of data from {@link Ext.form.BasicForm Form}s
63191  * and processes the returned response.</p>
63192  * <p>Instances of this class are only created by a {@link Ext.form.BasicForm Form} when
63193  * {@link Ext.form.BasicForm#submit submit}ting.</p>
63194  * <p><u><b>Response Packet Criteria</b></u></p>
63195  * <p>A response packet may contain:
63196  * <div class="mdetail-params"><ul>
63197  * <li><b><code>success</code></b> property : Boolean
63198  * <div class="sub-desc">The <code>success</code> property is required.</div></li>
63199  * <li><b><code>errors</code></b> property : Object
63200  * <div class="sub-desc"><div class="sub-desc">The <code>errors</code> property,
63201  * which is optional, contains error messages for invalid fields.</div></li>
63202  * </ul></div>
63203  * <p><u><b>JSON Packets</b></u></p>
63204  * <p>By default, response packets are assumed to be JSON, so a typical response
63205  * packet may look like this:</p><pre><code>
63206 {
63207     success: false,
63208     errors: {
63209         clientCode: "Client not found",
63210         portOfLoading: "This field must not be null"
63211     }
63212 }</code></pre>
63213  * <p>Other data may be placed into the response for processing by the {@link Ext.form.BasicForm}'s callback
63214  * or event handler methods. The object decoded from this JSON is available in the
63215  * {@link Ext.form.Action#result result} property.</p>
63216  * <p>Alternatively, if an {@link #errorReader} is specified as an {@link Ext.data.XmlReader XmlReader}:</p><pre><code>
63217     errorReader: new Ext.data.XmlReader({
63218             record : 'field',
63219             success: '@success'
63220         }, [
63221             'id', 'msg'
63222         ]
63223     )
63224 </code></pre>
63225  * <p>then the results may be sent back in XML format:</p><pre><code>
63226 &lt;?xml version="1.0" encoding="UTF-8"?&gt;
63227 &lt;message success="false"&gt;
63228 &lt;errors&gt;
63229     &lt;field&gt;
63230         &lt;id&gt;clientCode&lt;/id&gt;
63231         &lt;msg&gt;&lt;![CDATA[Code not found. &lt;br /&gt;&lt;i&gt;This is a test validation message from the server &lt;/i&gt;]]&gt;&lt;/msg&gt;
63232     &lt;/field&gt;
63233     &lt;field&gt;
63234         &lt;id&gt;portOfLoading&lt;/id&gt;
63235         &lt;msg&gt;&lt;![CDATA[Port not found. &lt;br /&gt;&lt;i&gt;This is a test validation message from the server &lt;/i&gt;]]&gt;&lt;/msg&gt;
63236     &lt;/field&gt;
63237 &lt;/errors&gt;
63238 &lt;/message&gt;
63239 </code></pre>
63240  * <p>Other elements may be placed into the response XML for processing by the {@link Ext.form.BasicForm}'s callback
63241  * or event handler methods. The XML document is available in the {@link #errorReader}'s {@link Ext.data.XmlReader#xmlData xmlData} property.</p>
63242  */
63243 Ext.form.Action.Submit = function(form, options){
63244     Ext.form.Action.Submit.superclass.constructor.call(this, form, options);
63245 };
63246
63247 Ext.extend(Ext.form.Action.Submit, Ext.form.Action, {
63248     /**
63249      * @cfg {Ext.data.DataReader} errorReader <p><b>Optional. JSON is interpreted with
63250      * no need for an errorReader.</b></p>
63251      * <p>A Reader which reads a single record from the returned data. The DataReader's
63252      * <b>success</b> property specifies how submission success is determined. The Record's
63253      * data provides the error messages to apply to any invalid form Fields.</p>
63254      */
63255     /**
63256      * @cfg {boolean} clientValidation Determines whether a Form's fields are validated
63257      * in a final call to {@link Ext.form.BasicForm#isValid isValid} prior to submission.
63258      * Pass <tt>false</tt> in the Form's submit options to prevent this. If not defined, pre-submission field validation
63259      * is performed.
63260      */
63261     type : 'submit',
63262
63263     // private
63264     run : function(){
63265         var o = this.options;
63266         var method = this.getMethod();
63267         var isGet = method == 'GET';
63268         if(o.clientValidation === false || this.form.isValid()){
63269             Ext.Ajax.request(Ext.apply(this.createCallback(o), {
63270                 form:this.form.el.dom,
63271                 url:this.getUrl(isGet),
63272                 method: method,
63273                 headers: o.headers,
63274                 params:!isGet ? this.getParams() : null,
63275                 isUpload: this.form.fileUpload
63276             }));
63277         }else if (o.clientValidation !== false){ // client validation failed
63278             this.failureType = Ext.form.Action.CLIENT_INVALID;
63279             this.form.afterAction(this, false);
63280         }
63281     },
63282
63283     // private
63284     success : function(response){
63285         var result = this.processResponse(response);
63286         if(result === true || result.success){
63287             this.form.afterAction(this, true);
63288             return;
63289         }
63290         if(result.errors){
63291             this.form.markInvalid(result.errors);
63292         }
63293         this.failureType = Ext.form.Action.SERVER_INVALID;
63294         this.form.afterAction(this, false);
63295     },
63296
63297     // private
63298     handleResponse : function(response){
63299         if(this.form.errorReader){
63300             var rs = this.form.errorReader.read(response);
63301             var errors = [];
63302             if(rs.records){
63303                 for(var i = 0, len = rs.records.length; i < len; i++) {
63304                     var r = rs.records[i];
63305                     errors[i] = r.data;
63306                 }
63307             }
63308             if(errors.length < 1){
63309                 errors = null;
63310             }
63311             return {
63312                 success : rs.success,
63313                 errors : errors
63314             };
63315         }
63316         return Ext.decode(response.responseText);
63317     }
63318 });
63319
63320
63321 /**
63322  * @class Ext.form.Action.Load
63323  * @extends Ext.form.Action
63324  * <p>A class which handles loading of data from a server into the Fields of an {@link Ext.form.BasicForm}.</p>
63325  * <p>Instances of this class are only created by a {@link Ext.form.BasicForm Form} when
63326  * {@link Ext.form.BasicForm#load load}ing.</p>
63327  * <p><u><b>Response Packet Criteria</b></u></p>
63328  * <p>A response packet <b>must</b> contain:
63329  * <div class="mdetail-params"><ul>
63330  * <li><b><code>success</code></b> property : Boolean</li>
63331  * <li><b><code>data</code></b> property : Object</li>
63332  * <div class="sub-desc">The <code>data</code> property contains the values of Fields to load.
63333  * The individual value object for each Field is passed to the Field's
63334  * {@link Ext.form.Field#setValue setValue} method.</div></li>
63335  * </ul></div>
63336  * <p><u><b>JSON Packets</b></u></p>
63337  * <p>By default, response packets are assumed to be JSON, so for the following form load call:<pre><code>
63338 var myFormPanel = new Ext.form.FormPanel({
63339     title: 'Client and routing info',
63340     items: [{
63341         fieldLabel: 'Client',
63342         name: 'clientName'
63343     }, {
63344         fieldLabel: 'Port of loading',
63345         name: 'portOfLoading'
63346     }, {
63347         fieldLabel: 'Port of discharge',
63348         name: 'portOfDischarge'
63349     }]
63350 });
63351 myFormPanel.{@link Ext.form.FormPanel#getForm getForm}().{@link Ext.form.BasicForm#load load}({
63352     url: '/getRoutingInfo.php',
63353     params: {
63354         consignmentRef: myConsignmentRef
63355     },
63356     failure: function(form, action) {
63357         Ext.Msg.alert("Load failed", action.result.errorMessage);
63358     }
63359 });
63360 </code></pre>
63361  * a <b>success response</b> packet may look like this:</p><pre><code>
63362 {
63363     success: true,
63364     data: {
63365         clientName: "Fred. Olsen Lines",
63366         portOfLoading: "FXT",
63367         portOfDischarge: "OSL"
63368     }
63369 }</code></pre>
63370  * while a <b>failure response</b> packet may look like this:</p><pre><code>
63371 {
63372     success: false,
63373     errorMessage: "Consignment reference not found"
63374 }</code></pre>
63375  * <p>Other data may be placed into the response for processing the {@link Ext.form.BasicForm Form}'s
63376  * callback or event handler methods. The object decoded from this JSON is available in the
63377  * {@link Ext.form.Action#result result} property.</p>
63378  */
63379 Ext.form.Action.Load = function(form, options){
63380     Ext.form.Action.Load.superclass.constructor.call(this, form, options);
63381     this.reader = this.form.reader;
63382 };
63383
63384 Ext.extend(Ext.form.Action.Load, Ext.form.Action, {
63385     // private
63386     type : 'load',
63387
63388     // private
63389     run : function(){
63390         Ext.Ajax.request(Ext.apply(
63391                 this.createCallback(this.options), {
63392                     method:this.getMethod(),
63393                     url:this.getUrl(false),
63394                     headers: this.options.headers,
63395                     params:this.getParams()
63396         }));
63397     },
63398
63399     // private
63400     success : function(response){
63401         var result = this.processResponse(response);
63402         if(result === true || !result.success || !result.data){
63403             this.failureType = Ext.form.Action.LOAD_FAILURE;
63404             this.form.afterAction(this, false);
63405             return;
63406         }
63407         this.form.clearInvalid();
63408         this.form.setValues(result.data);
63409         this.form.afterAction(this, true);
63410     },
63411
63412     // private
63413     handleResponse : function(response){
63414         if(this.form.reader){
63415             var rs = this.form.reader.read(response);
63416             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
63417             return {
63418                 success : rs.success,
63419                 data : data
63420             };
63421         }
63422         return Ext.decode(response.responseText);
63423     }
63424 });
63425
63426
63427
63428 /**
63429  * @class Ext.form.Action.DirectLoad
63430  * @extends Ext.form.Action.Load
63431  * <p>Provides Ext.direct support for loading form data.</p>
63432  * <p>This example illustrates usage of Ext.Direct to <b>load</b> a form through Ext.Direct.</p>
63433  * <pre><code>
63434 var myFormPanel = new Ext.form.FormPanel({
63435     // configs for FormPanel
63436     title: 'Basic Information',
63437     renderTo: document.body,
63438     width: 300, height: 160,
63439     padding: 10,
63440
63441     // configs apply to child items
63442     defaults: {anchor: '100%'},
63443     defaultType: 'textfield',
63444     items: [{
63445         fieldLabel: 'Name',
63446         name: 'name'
63447     },{
63448         fieldLabel: 'Email',
63449         name: 'email'
63450     },{
63451         fieldLabel: 'Company',
63452         name: 'company'
63453     }],
63454
63455     // configs for BasicForm
63456     api: {
63457         // The server-side method to call for load() requests
63458         load: Profile.getBasicInfo,
63459         // The server-side must mark the submit handler as a 'formHandler'
63460         submit: Profile.updateBasicInfo
63461     },
63462     // specify the order for the passed params
63463     paramOrder: ['uid', 'foo']
63464 });
63465
63466 // load the form
63467 myFormPanel.getForm().load({
63468     // pass 2 arguments to server side getBasicInfo method (len=2)
63469     params: {
63470         foo: 'bar',
63471         uid: 34
63472     }
63473 });
63474  * </code></pre>
63475  * The data packet sent to the server will resemble something like:
63476  * <pre><code>
63477 [
63478     {
63479         "action":"Profile","method":"getBasicInfo","type":"rpc","tid":2,
63480         "data":[34,"bar"] // note the order of the params
63481     }
63482 ]
63483  * </code></pre>
63484  * The form will process a data packet returned by the server that is similar
63485  * to the following format:
63486  * <pre><code>
63487 [
63488     {
63489         "action":"Profile","method":"getBasicInfo","type":"rpc","tid":2,
63490         "result":{
63491             "success":true,
63492             "data":{
63493                 "name":"Fred Flintstone",
63494                 "company":"Slate Rock and Gravel",
63495                 "email":"fred.flintstone@slaterg.com"
63496             }
63497         }
63498     }
63499 ]
63500  * </code></pre>
63501  */
63502 Ext.form.Action.DirectLoad = Ext.extend(Ext.form.Action.Load, {
63503     constructor: function(form, opts) {
63504         Ext.form.Action.DirectLoad.superclass.constructor.call(this, form, opts);
63505     },
63506     type : 'directload',
63507
63508     run : function(){
63509         var args = this.getParams();
63510         args.push(this.success, this);
63511         this.form.api.load.apply(window, args);
63512     },
63513
63514     getParams : function() {
63515         var buf = [], o = {};
63516         var bp = this.form.baseParams;
63517         var p = this.options.params;
63518         Ext.apply(o, p, bp);
63519         var paramOrder = this.form.paramOrder;
63520         if(paramOrder){
63521             for(var i = 0, len = paramOrder.length; i < len; i++){
63522                 buf.push(o[paramOrder[i]]);
63523             }
63524         }else if(this.form.paramsAsHash){
63525             buf.push(o);
63526         }
63527         return buf;
63528     },
63529     // Direct actions have already been processed and therefore
63530     // we can directly set the result; Direct Actions do not have
63531     // a this.response property.
63532     processResponse : function(result) {
63533         this.result = result;
63534         return result;
63535     },
63536     
63537     success : function(response, trans){
63538         if(trans.type == Ext.Direct.exceptions.SERVER){
63539             response = {};
63540         }
63541         Ext.form.Action.DirectLoad.superclass.success.call(this, response);
63542     }
63543 });
63544
63545 /**
63546  * @class Ext.form.Action.DirectSubmit
63547  * @extends Ext.form.Action.Submit
63548  * <p>Provides Ext.direct support for submitting form data.</p>
63549  * <p>This example illustrates usage of Ext.Direct to <b>submit</b> a form through Ext.Direct.</p>
63550  * <pre><code>
63551 var myFormPanel = new Ext.form.FormPanel({
63552     // configs for FormPanel
63553     title: 'Basic Information',
63554     renderTo: document.body,
63555     width: 300, height: 160,
63556     padding: 10,
63557     buttons:[{
63558         text: 'Submit',
63559         handler: function(){
63560             myFormPanel.getForm().submit({
63561                 params: {
63562                     foo: 'bar',
63563                     uid: 34
63564                 }
63565             });
63566         }
63567     }],
63568
63569     // configs apply to child items
63570     defaults: {anchor: '100%'},
63571     defaultType: 'textfield',
63572     items: [{
63573         fieldLabel: 'Name',
63574         name: 'name'
63575     },{
63576         fieldLabel: 'Email',
63577         name: 'email'
63578     },{
63579         fieldLabel: 'Company',
63580         name: 'company'
63581     }],
63582
63583     // configs for BasicForm
63584     api: {
63585         // The server-side method to call for load() requests
63586         load: Profile.getBasicInfo,
63587         // The server-side must mark the submit handler as a 'formHandler'
63588         submit: Profile.updateBasicInfo
63589     },
63590     // specify the order for the passed params
63591     paramOrder: ['uid', 'foo']
63592 });
63593  * </code></pre>
63594  * The data packet sent to the server will resemble something like:
63595  * <pre><code>
63596 {
63597     "action":"Profile","method":"updateBasicInfo","type":"rpc","tid":"6",
63598     "result":{
63599         "success":true,
63600         "id":{
63601             "extAction":"Profile","extMethod":"updateBasicInfo",
63602             "extType":"rpc","extTID":"6","extUpload":"false",
63603             "name":"Aaron Conran","email":"aaron@extjs.com","company":"Ext JS, LLC"
63604         }
63605     }
63606 }
63607  * </code></pre>
63608  * The form will process a data packet returned by the server that is similar
63609  * to the following:
63610  * <pre><code>
63611 // sample success packet (batched requests)
63612 [
63613     {
63614         "action":"Profile","method":"updateBasicInfo","type":"rpc","tid":3,
63615         "result":{
63616             "success":true
63617         }
63618     }
63619 ]
63620
63621 // sample failure packet (one request)
63622 {
63623         "action":"Profile","method":"updateBasicInfo","type":"rpc","tid":"6",
63624         "result":{
63625             "errors":{
63626                 "email":"already taken"
63627             },
63628             "success":false,
63629             "foo":"bar"
63630         }
63631 }
63632  * </code></pre>
63633  * Also see the discussion in {@link Ext.form.Action.DirectLoad}.
63634  */
63635 Ext.form.Action.DirectSubmit = Ext.extend(Ext.form.Action.Submit, {
63636     constructor : function(form, opts) {
63637         Ext.form.Action.DirectSubmit.superclass.constructor.call(this, form, opts);
63638     },
63639     type : 'directsubmit',
63640     // override of Submit
63641     run : function(){
63642         var o = this.options;
63643         if(o.clientValidation === false || this.form.isValid()){
63644             // tag on any additional params to be posted in the
63645             // form scope
63646             this.success.params = this.getParams();
63647             this.form.api.submit(this.form.el.dom, this.success, this);
63648         }else if (o.clientValidation !== false){ // client validation failed
63649             this.failureType = Ext.form.Action.CLIENT_INVALID;
63650             this.form.afterAction(this, false);
63651         }
63652     },
63653
63654     getParams : function() {
63655         var o = {};
63656         var bp = this.form.baseParams;
63657         var p = this.options.params;
63658         Ext.apply(o, p, bp);
63659         return o;
63660     },
63661     // Direct actions have already been processed and therefore
63662     // we can directly set the result; Direct Actions do not have
63663     // a this.response property.
63664     processResponse : function(result) {
63665         this.result = result;
63666         return result;
63667     },
63668     
63669     success : function(response, trans){
63670         if(trans.type == Ext.Direct.exceptions.SERVER){
63671             response = {};
63672         }
63673         Ext.form.Action.DirectSubmit.superclass.success.call(this, response);
63674     }
63675 });
63676
63677 Ext.form.Action.ACTION_TYPES = {
63678     'load' : Ext.form.Action.Load,
63679     'submit' : Ext.form.Action.Submit,
63680     'directload' : Ext.form.Action.DirectLoad,
63681     'directsubmit' : Ext.form.Action.DirectSubmit
63682 };
63683 /**
63684  * @class Ext.form.VTypes
63685  * <p>This is a singleton object which contains a set of commonly used field validation functions.
63686  * The validations provided are basic and intended to be easily customizable and extended.</p>
63687  * <p>To add custom VTypes specify the <code>{@link Ext.form.TextField#vtype vtype}</code> validation
63688  * test function, and optionally specify any corresponding error text to display and any keystroke
63689  * filtering mask to apply. For example:</p>
63690  * <pre><code>
63691 // custom Vtype for vtype:'time'
63692 var timeTest = /^([1-9]|1[0-9]):([0-5][0-9])(\s[a|p]m)$/i;
63693 Ext.apply(Ext.form.VTypes, {
63694     //  vtype validation function
63695     time: function(val, field) {
63696         return timeTest.test(val);
63697     },
63698     // vtype Text property: The error text to display when the validation function returns false
63699     timeText: 'Not a valid time.  Must be in the format "12:34 PM".',
63700     // vtype Mask property: The keystroke filter mask
63701     timeMask: /[\d\s:amp]/i
63702 });
63703  * </code></pre>
63704  * Another example: 
63705  * <pre><code>
63706 // custom Vtype for vtype:'IPAddress'
63707 Ext.apply(Ext.form.VTypes, {
63708     IPAddress:  function(v) {
63709         return /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(v);
63710     },
63711     IPAddressText: 'Must be a numeric IP address',
63712     IPAddressMask: /[\d\.]/i
63713 });
63714  * </code></pre>
63715  * @singleton
63716  */
63717 Ext.form.VTypes = function(){
63718     // closure these in so they are only created once.
63719     var alpha = /^[a-zA-Z_]+$/,
63720         alphanum = /^[a-zA-Z0-9_]+$/,
63721         email = /^(\w+)([\-+.][\w]+)*@(\w[\-\w]*\.){1,5}([A-Za-z]){2,6}$/,
63722         url = /(((^https?)|(^ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
63723
63724     // All these messages and functions are configurable
63725     return {
63726         /**
63727          * The function used to validate email addresses.  Note that this is a very basic validation -- complete
63728          * validation per the email RFC specifications is very complex and beyond the scope of this class, although
63729          * this function can be overridden if a more comprehensive validation scheme is desired.  See the validation
63730          * section of the <a href="http://en.wikipedia.org/wiki/E-mail_address">Wikipedia article on email addresses</a> 
63731          * for additional information.  This implementation is intended to validate the following emails:<tt>
63732          * 'barney@example.de', 'barney.rubble@example.com', 'barney-rubble@example.coop', 'barney+rubble@example.com'
63733          * </tt>.
63734          * @param {String} value The email address
63735          * @return {Boolean} true if the RegExp test passed, and false if not.
63736          */
63737         'email' : function(v){
63738             return email.test(v);
63739         },
63740         /**
63741          * The error text to display when the email validation function returns false.  Defaults to:
63742          * <tt>'This field should be an e-mail address in the format "user@example.com"'</tt>
63743          * @type String
63744          */
63745         'emailText' : 'This field should be an e-mail address in the format "user@example.com"',
63746         /**
63747          * The keystroke filter mask to be applied on email input.  See the {@link #email} method for 
63748          * information about more complex email validation. Defaults to:
63749          * <tt>/[a-z0-9_\.\-@]/i</tt>
63750          * @type RegExp
63751          */
63752         'emailMask' : /[a-z0-9_\.\-@]/i,
63753
63754         /**
63755          * The function used to validate URLs
63756          * @param {String} value The URL
63757          * @return {Boolean} true if the RegExp test passed, and false if not.
63758          */
63759         'url' : function(v){
63760             return url.test(v);
63761         },
63762         /**
63763          * The error text to display when the url validation function returns false.  Defaults to:
63764          * <tt>'This field should be a URL in the format "http:/'+'/www.example.com"'</tt>
63765          * @type String
63766          */
63767         'urlText' : 'This field should be a URL in the format "http:/'+'/www.example.com"',
63768         
63769         /**
63770          * The function used to validate alpha values
63771          * @param {String} value The value
63772          * @return {Boolean} true if the RegExp test passed, and false if not.
63773          */
63774         'alpha' : function(v){
63775             return alpha.test(v);
63776         },
63777         /**
63778          * The error text to display when the alpha validation function returns false.  Defaults to:
63779          * <tt>'This field should only contain letters and _'</tt>
63780          * @type String
63781          */
63782         'alphaText' : 'This field should only contain letters and _',
63783         /**
63784          * The keystroke filter mask to be applied on alpha input.  Defaults to:
63785          * <tt>/[a-z_]/i</tt>
63786          * @type RegExp
63787          */
63788         'alphaMask' : /[a-z_]/i,
63789
63790         /**
63791          * The function used to validate alphanumeric values
63792          * @param {String} value The value
63793          * @return {Boolean} true if the RegExp test passed, and false if not.
63794          */
63795         'alphanum' : function(v){
63796             return alphanum.test(v);
63797         },
63798         /**
63799          * The error text to display when the alphanumeric validation function returns false.  Defaults to:
63800          * <tt>'This field should only contain letters, numbers and _'</tt>
63801          * @type String
63802          */
63803         'alphanumText' : 'This field should only contain letters, numbers and _',
63804         /**
63805          * The keystroke filter mask to be applied on alphanumeric input.  Defaults to:
63806          * <tt>/[a-z0-9_]/i</tt>
63807          * @type RegExp
63808          */
63809         'alphanumMask' : /[a-z0-9_]/i
63810     };
63811 }();/**\r
63812  * @class Ext.grid.GridPanel\r
63813  * @extends Ext.Panel\r
63814  * <p>This class represents the primary interface of a component based grid control to represent data\r
63815  * in a tabular format of rows and columns. The GridPanel is composed of the following:</p>\r
63816  * <div class="mdetail-params"><ul>\r
63817  * <li><b>{@link Ext.data.Store Store}</b> : The Model holding the data records (rows)\r
63818  * <div class="sub-desc"></div></li>\r
63819  * <li><b>{@link Ext.grid.ColumnModel Column model}</b> : Column makeup\r
63820  * <div class="sub-desc"></div></li>\r
63821  * <li><b>{@link Ext.grid.GridView View}</b> : Encapsulates the user interface \r
63822  * <div class="sub-desc"></div></li>\r
63823  * <li><b>{@link Ext.grid.AbstractSelectionModel selection model}</b> : Selection behavior \r
63824  * <div class="sub-desc"></div></li>\r
63825  * </ul></div>\r
63826  * <p>Example usage:</p>\r
63827  * <pre><code>\r
63828 var grid = new Ext.grid.GridPanel({\r
63829     {@link #store}: new {@link Ext.data.Store}({\r
63830         {@link Ext.data.Store#autoDestroy autoDestroy}: true,\r
63831         {@link Ext.data.Store#reader reader}: reader,\r
63832         {@link Ext.data.Store#data data}: xg.dummyData\r
63833     }),\r
63834     {@link #colModel}: new {@link Ext.grid.ColumnModel}({\r
63835         {@link Ext.grid.ColumnModel#defaults defaults}: {\r
63836             width: 120,\r
63837             sortable: true\r
63838         },\r
63839         {@link Ext.grid.ColumnModel#columns columns}: [\r
63840             {id: 'company', header: 'Company', width: 200, sortable: true, dataIndex: 'company'},\r
63841             {header: 'Price', renderer: Ext.util.Format.usMoney, dataIndex: 'price'},\r
63842             {header: 'Change', dataIndex: 'change'},\r
63843             {header: '% Change', dataIndex: 'pctChange'},\r
63844             // instead of specifying renderer: Ext.util.Format.dateRenderer('m/d/Y') use xtype\r
63845             {\r
63846                 header: 'Last Updated', width: 135, dataIndex: 'lastChange',\r
63847                 xtype: 'datecolumn', format: 'M d, Y'\r
63848             }\r
63849         ],\r
63850     }),\r
63851     {@link #viewConfig}: {\r
63852         {@link Ext.grid.GridView#forceFit forceFit}: true,\r
63853 \r
63854 //      Return CSS class to apply to rows depending upon data values\r
63855         {@link Ext.grid.GridView#getRowClass getRowClass}: function(record, index) {\r
63856             var c = record.{@link Ext.data.Record#get get}('change');\r
63857             if (c < 0) {\r
63858                 return 'price-fall';\r
63859             } else if (c > 0) {\r
63860                 return 'price-rise';\r
63861             }\r
63862         }\r
63863     },\r
63864     {@link #sm}: new Ext.grid.RowSelectionModel({singleSelect:true}),\r
63865     width: 600,\r
63866     height: 300,\r
63867     frame: true,\r
63868     title: 'Framed with Row Selection and Horizontal Scrolling',\r
63869     iconCls: 'icon-grid'\r
63870 });\r
63871  * </code></pre>\r
63872  * <p><b><u>Notes:</u></b></p>\r
63873  * <div class="mdetail-params"><ul>\r
63874  * <li>Although this class inherits many configuration options from base classes, some of them\r
63875  * (such as autoScroll, autoWidth, layout, items, etc) are not used by this class, and will\r
63876  * have no effect.</li>\r
63877  * <li>A grid <b>requires</b> a width in which to scroll its columns, and a height in which to\r
63878  * scroll its rows. These dimensions can either be set explicitly through the\r
63879  * <tt>{@link Ext.BoxComponent#height height}</tt> and <tt>{@link Ext.BoxComponent#width width}</tt>\r
63880  * configuration options or implicitly set by using the grid as a child item of a\r
63881  * {@link Ext.Container Container} which will have a {@link Ext.Container#layout layout manager}\r
63882  * provide the sizing of its child items (for example the Container of the Grid may specify\r
63883  * <tt>{@link Ext.Container#layout layout}:'fit'</tt>).</li>\r
63884  * <li>To access the data in a Grid, it is necessary to use the data model encapsulated\r
63885  * by the {@link #store Store}. See the {@link #cellclick} event for more details.</li>\r
63886  * </ul></div>\r
63887  * @constructor\r
63888  * @param {Object} config The config object\r
63889  * @xtype grid\r
63890  */\r
63891 Ext.grid.GridPanel = Ext.extend(Ext.Panel, {\r
63892     /**\r
63893      * @cfg {String} autoExpandColumn\r
63894      * <p>The <tt>{@link Ext.grid.Column#id id}</tt> of a {@link Ext.grid.Column column} in\r
63895      * this grid that should expand to fill unused space. This value specified here can not\r
63896      * be <tt>0</tt>.</p>\r
63897      * <br><p><b>Note</b>: If the Grid's {@link Ext.grid.GridView view} is configured with\r
63898      * <tt>{@link Ext.grid.GridView#forceFit forceFit}=true</tt> the <tt>autoExpandColumn</tt>\r
63899      * is ignored. See {@link Ext.grid.Column}.<tt>{@link Ext.grid.Column#width width}</tt>\r
63900      * for additional details.</p>\r
63901      * <p>See <tt>{@link #autoExpandMax}</tt> and <tt>{@link #autoExpandMin}</tt> also.</p>\r
63902      */\r
63903     autoExpandColumn : false,\r
63904     /**\r
63905      * @cfg {Number} autoExpandMax The maximum width the <tt>{@link #autoExpandColumn}</tt>\r
63906      * can have (if enabled). Defaults to <tt>1000</tt>.\r
63907      */\r
63908     autoExpandMax : 1000,\r
63909     /**\r
63910      * @cfg {Number} autoExpandMin The minimum width the <tt>{@link #autoExpandColumn}</tt>\r
63911      * can have (if enabled). Defaults to <tt>50</tt>.\r
63912      */\r
63913     autoExpandMin : 50,\r
63914     /**\r
63915      * @cfg {Boolean} columnLines <tt>true</tt> to add css for column separation lines.\r
63916      * Default is <tt>false</tt>.\r
63917      */\r
63918     columnLines : false,\r
63919     /**\r
63920      * @cfg {Object} cm Shorthand for <tt>{@link #colModel}</tt>.\r
63921      */\r
63922     /**\r
63923      * @cfg {Object} colModel The {@link Ext.grid.ColumnModel} to use when rendering the grid (required).\r
63924      */\r
63925     /**\r
63926      * @cfg {Array} columns An array of {@link Ext.grid.Column columns} to auto create a\r
63927      * {@link Ext.grid.ColumnModel}.  The ColumnModel may be explicitly created via the\r
63928      * <tt>{@link #colModel}</tt> configuration property.\r
63929      */\r
63930     /**\r
63931      * @cfg {String} ddGroup The DD group this GridPanel belongs to. Defaults to <tt>'GridDD'</tt> if not specified.\r
63932      */\r
63933     /**\r
63934      * @cfg {String} ddText\r
63935      * Configures the text in the drag proxy.  Defaults to:\r
63936      * <pre><code>\r
63937      * ddText : '{0} selected row{1}'\r
63938      * </code></pre>\r
63939      * <tt>{0}</tt> is replaced with the number of selected rows.\r
63940      */\r
63941     ddText : '{0} selected row{1}',\r
63942     /**\r
63943      * @cfg {Boolean} deferRowRender <P>Defaults to <tt>true</tt> to enable deferred row rendering.</p>\r
63944      * <p>This allows the GridPanel to be initially rendered empty, with the expensive update of the row\r
63945      * structure deferred so that layouts with GridPanels appear more quickly.</p>\r
63946      */\r
63947     deferRowRender : true,\r
63948     /**\r
63949      * @cfg {Boolean} disableSelection <p><tt>true</tt> to disable selections in the grid. Defaults to <tt>false</tt>.</p>\r
63950      * <p>Ignored if a {@link #selModel SelectionModel} is specified.</p>\r
63951      */\r
63952     /**\r
63953      * @cfg {Boolean} enableColumnResize <tt>false</tt> to turn off column resizing for the whole grid. Defaults to <tt>true</tt>.\r
63954      */\r
63955     /**\r
63956      * @cfg {Boolean} enableColumnHide\r
63957      * Defaults to <tt>true</tt> to enable {@link Ext.grid.Column#hidden hiding of columns}\r
63958      * with the {@link #enableHdMenu header menu}.\r
63959      */\r
63960     enableColumnHide : true,\r
63961     /**\r
63962      * @cfg {Boolean} enableColumnMove Defaults to <tt>true</tt> to enable drag and drop reorder of columns. <tt>false</tt>\r
63963      * to turn off column reordering via drag drop.\r
63964      */\r
63965     enableColumnMove : true,\r
63966     /**\r
63967      * @cfg {Boolean} enableDragDrop <p>Enables dragging of the selected rows of the GridPanel. Defaults to <tt>false</tt>.</p>\r
63968      * <p>Setting this to <b><tt>true</tt></b> causes this GridPanel's {@link #getView GridView} to\r
63969      * create an instance of {@link Ext.grid.GridDragZone}. <b>Note</b>: this is available only <b>after</b>\r
63970      * the Grid has been rendered as the GridView's <tt>{@link Ext.grid.GridView#dragZone dragZone}</tt>\r
63971      * property.</p>\r
63972      * <p>A cooperating {@link Ext.dd.DropZone DropZone} must be created who's implementations of\r
63973      * {@link Ext.dd.DropZone#onNodeEnter onNodeEnter}, {@link Ext.dd.DropZone#onNodeOver onNodeOver},\r
63974      * {@link Ext.dd.DropZone#onNodeOut onNodeOut} and {@link Ext.dd.DropZone#onNodeDrop onNodeDrop} are able\r
63975      * to process the {@link Ext.grid.GridDragZone#getDragData data} which is provided.</p>\r
63976      */\r
63977     enableDragDrop : false,\r
63978     /**\r
63979      * @cfg {Boolean} enableHdMenu Defaults to <tt>true</tt> to enable the drop down button for menu in the headers.\r
63980      */\r
63981     enableHdMenu : true,\r
63982     /**\r
63983      * @cfg {Boolean} hideHeaders True to hide the grid's header. Defaults to <code>false</code>.\r
63984      */\r
63985     /**\r
63986      * @cfg {Object} loadMask An {@link Ext.LoadMask} config or true to mask the grid while\r
63987      * loading. Defaults to <code>false</code>.\r
63988      */\r
63989     loadMask : false,\r
63990     /**\r
63991      * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if <tt>autoHeight</tt> is not on.\r
63992      */\r
63993     /**\r
63994      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Defaults to <tt>25</tt>.\r
63995      */\r
63996     minColumnWidth : 25,\r
63997     /**\r
63998      * @cfg {Object} sm Shorthand for <tt>{@link #selModel}</tt>.\r
63999      */\r
64000     /**\r
64001      * @cfg {Object} selModel Any subclass of {@link Ext.grid.AbstractSelectionModel} that will provide\r
64002      * the selection model for the grid (defaults to {@link Ext.grid.RowSelectionModel} if not specified).\r
64003      */\r
64004     /**\r
64005      * @cfg {Ext.data.Store} store The {@link Ext.data.Store} the grid should use as its data source (required).\r
64006      */\r
64007     /**\r
64008      * @cfg {Boolean} stripeRows <tt>true</tt> to stripe the rows. Default is <tt>false</tt>.\r
64009      * <p>This causes the CSS class <tt><b>x-grid3-row-alt</b></tt> to be added to alternate rows of\r
64010      * the grid. A default CSS rule is provided which sets a background colour, but you can override this\r
64011      * with a rule which either overrides the <b>background-color</b> style using the '!important'\r
64012      * modifier, or which uses a CSS selector of higher specificity.</p>\r
64013      */\r
64014     stripeRows : false,\r
64015     /**\r
64016      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is <tt>true</tt>\r
64017      * for GridPanel, but <tt>false</tt> for EditorGridPanel.\r
64018      */\r
64019     trackMouseOver : true,\r
64020     /**\r
64021      * @cfg {Array} stateEvents\r
64022      * An array of events that, when fired, should trigger this component to save its state.\r
64023      * Defaults to:<pre><code>\r
64024      * stateEvents: ['columnmove', 'columnresize', 'sortchange', 'groupchange']\r
64025      * </code></pre>\r
64026      * <p>These can be any types of events supported by this component, including browser or\r
64027      * custom events (e.g., <tt>['click', 'customerchange']</tt>).</p>\r
64028      * <p>See {@link Ext.Component#stateful} for an explanation of saving and restoring\r
64029      * Component state.</p>\r
64030      */\r
64031     stateEvents : ['columnmove', 'columnresize', 'sortchange', 'groupchange'],\r
64032     /**\r
64033      * @cfg {Object} view The {@link Ext.grid.GridView} used by the grid. This can be set\r
64034      * before a call to {@link Ext.Component#render render()}.\r
64035      */\r
64036     view : null,\r
64037     \r
64038     /**\r
64039      * @cfg {Array} bubbleEvents\r
64040      * <p>An array of events that, when fired, should be bubbled to any parent container.\r
64041      * See {@link Ext.util.Observable#enableBubble}. \r
64042      * Defaults to <tt>[]</tt>.\r
64043      */\r
64044     bubbleEvents: [],\r
64045     \r
64046     /**\r
64047      * @cfg {Object} viewConfig A config object that will be applied to the grid's UI view.  Any of\r
64048      * the config options available for {@link Ext.grid.GridView} can be specified here. This option\r
64049      * is ignored if <tt>{@link #view}</tt> is specified.\r
64050      */\r
64051 \r
64052     // private\r
64053     rendered : false,\r
64054     // private\r
64055     viewReady : false,\r
64056 \r
64057     // private\r
64058     initComponent : function(){\r
64059         Ext.grid.GridPanel.superclass.initComponent.call(this);\r
64060 \r
64061         if(this.columnLines){\r
64062             this.cls = (this.cls || '') + ' x-grid-with-col-lines';\r
64063         }\r
64064         // override any provided value since it isn't valid\r
64065         // and is causing too many bug reports ;)\r
64066         this.autoScroll = false;\r
64067         this.autoWidth = false;\r
64068 \r
64069         if(Ext.isArray(this.columns)){\r
64070             this.colModel = new Ext.grid.ColumnModel(this.columns);\r
64071             delete this.columns;\r
64072         }\r
64073 \r
64074         // check and correct shorthanded configs\r
64075         if(this.ds){\r
64076             this.store = this.ds;\r
64077             delete this.ds;\r
64078         }\r
64079         if(this.cm){\r
64080             this.colModel = this.cm;\r
64081             delete this.cm;\r
64082         }\r
64083         if(this.sm){\r
64084             this.selModel = this.sm;\r
64085             delete this.sm;\r
64086         }\r
64087         this.store = Ext.StoreMgr.lookup(this.store);\r
64088 \r
64089         this.addEvents(\r
64090             // raw events\r
64091             /**\r
64092              * @event click\r
64093              * The raw click event for the entire grid.\r
64094              * @param {Ext.EventObject} e\r
64095              */\r
64096             'click',\r
64097             /**\r
64098              * @event dblclick\r
64099              * The raw dblclick event for the entire grid.\r
64100              * @param {Ext.EventObject} e\r
64101              */\r
64102             'dblclick',\r
64103             /**\r
64104              * @event contextmenu\r
64105              * The raw contextmenu event for the entire grid.\r
64106              * @param {Ext.EventObject} e\r
64107              */\r
64108             'contextmenu',\r
64109             /**\r
64110              * @event mousedown\r
64111              * The raw mousedown event for the entire grid.\r
64112              * @param {Ext.EventObject} e\r
64113              */\r
64114             'mousedown',\r
64115             /**\r
64116              * @event mouseup\r
64117              * The raw mouseup event for the entire grid.\r
64118              * @param {Ext.EventObject} e\r
64119              */\r
64120             'mouseup',\r
64121             /**\r
64122              * @event mouseover\r
64123              * The raw mouseover event for the entire grid.\r
64124              * @param {Ext.EventObject} e\r
64125              */\r
64126             'mouseover',\r
64127             /**\r
64128              * @event mouseout\r
64129              * The raw mouseout event for the entire grid.\r
64130              * @param {Ext.EventObject} e\r
64131              */\r
64132             'mouseout',\r
64133             /**\r
64134              * @event keypress\r
64135              * The raw keypress event for the entire grid.\r
64136              * @param {Ext.EventObject} e\r
64137              */\r
64138             'keypress',\r
64139             /**\r
64140              * @event keydown\r
64141              * The raw keydown event for the entire grid.\r
64142              * @param {Ext.EventObject} e\r
64143              */\r
64144             'keydown',\r
64145 \r
64146             // custom events\r
64147             /**\r
64148              * @event cellmousedown\r
64149              * Fires before a cell is clicked\r
64150              * @param {Grid} this\r
64151              * @param {Number} rowIndex\r
64152              * @param {Number} columnIndex\r
64153              * @param {Ext.EventObject} e\r
64154              */\r
64155             'cellmousedown',\r
64156             /**\r
64157              * @event rowmousedown\r
64158              * Fires before a row is clicked\r
64159              * @param {Grid} this\r
64160              * @param {Number} rowIndex\r
64161              * @param {Ext.EventObject} e\r
64162              */\r
64163             'rowmousedown',\r
64164             /**\r
64165              * @event headermousedown\r
64166              * Fires before a header is clicked\r
64167              * @param {Grid} this\r
64168              * @param {Number} columnIndex\r
64169              * @param {Ext.EventObject} e\r
64170              */\r
64171             'headermousedown',\r
64172             \r
64173             /**\r
64174              * @event groupmousedown\r
64175              * Fires before a group header is clicked. <b>Only applies for grids with a {@link Ext.grid.GroupingView GroupingView}</b>.\r
64176              * @param {Grid} this\r
64177              * @param {String} groupField\r
64178              * @param {String} groupValue\r
64179              * @param {Ext.EventObject} e\r
64180              */\r
64181             'groupmousedown',\r
64182             \r
64183             /**\r
64184              * @event rowbodymousedown\r
64185              * Fires before the row body is clicked. <b>Only applies for grids with {@link Ext.grid.GridView#enableRowBody enableRowBody} configured.</b>\r
64186              * @param {Grid} this\r
64187              * @param {Number} rowIndex\r
64188              * @param {Ext.EventObject} e\r
64189              */\r
64190             'rowbodymousedown',\r
64191             \r
64192             /**\r
64193              * @event containermousedown\r
64194              * Fires before the container is clicked. The container consists of any part of the grid body that is not covered by a row.\r
64195              * @param {Grid} this\r
64196              * @param {Ext.EventObject} e\r
64197              */\r
64198             'containermousedown',\r
64199 \r
64200             /**\r
64201              * @event cellclick\r
64202              * Fires when a cell is clicked.\r
64203              * The data for the cell is drawn from the {@link Ext.data.Record Record}\r
64204              * for this row. To access the data in the listener function use the\r
64205              * following technique:\r
64206              * <pre><code>\r
64207 function(grid, rowIndex, columnIndex, e) {\r
64208     var record = grid.getStore().getAt(rowIndex);  // Get the Record\r
64209     var fieldName = grid.getColumnModel().getDataIndex(columnIndex); // Get field name\r
64210     var data = record.get(fieldName);\r
64211 }\r
64212 </code></pre>\r
64213              * @param {Grid} this\r
64214              * @param {Number} rowIndex\r
64215              * @param {Number} columnIndex\r
64216              * @param {Ext.EventObject} e\r
64217              */\r
64218             'cellclick',\r
64219             /**\r
64220              * @event celldblclick\r
64221              * Fires when a cell is double clicked\r
64222              * @param {Grid} this\r
64223              * @param {Number} rowIndex\r
64224              * @param {Number} columnIndex\r
64225              * @param {Ext.EventObject} e\r
64226              */\r
64227             'celldblclick',\r
64228             /**\r
64229              * @event rowclick\r
64230              * Fires when a row is clicked\r
64231              * @param {Grid} this\r
64232              * @param {Number} rowIndex\r
64233              * @param {Ext.EventObject} e\r
64234              */\r
64235             'rowclick',\r
64236             /**\r
64237              * @event rowdblclick\r
64238              * Fires when a row is double clicked\r
64239              * @param {Grid} this\r
64240              * @param {Number} rowIndex\r
64241              * @param {Ext.EventObject} e\r
64242              */\r
64243             'rowdblclick',\r
64244             /**\r
64245              * @event headerclick\r
64246              * Fires when a header is clicked\r
64247              * @param {Grid} this\r
64248              * @param {Number} columnIndex\r
64249              * @param {Ext.EventObject} e\r
64250              */\r
64251             'headerclick',\r
64252             /**\r
64253              * @event headerdblclick\r
64254              * Fires when a header cell is double clicked\r
64255              * @param {Grid} this\r
64256              * @param {Number} columnIndex\r
64257              * @param {Ext.EventObject} e\r
64258              */\r
64259             'headerdblclick',\r
64260             /**\r
64261              * @event groupclick\r
64262              * Fires when group header is clicked. <b>Only applies for grids with a {@link Ext.grid.GroupingView GroupingView}</b>.\r
64263              * @param {Grid} this\r
64264              * @param {String} groupField\r
64265              * @param {String} groupValue\r
64266              * @param {Ext.EventObject} e\r
64267              */\r
64268             'groupclick',\r
64269             /**\r
64270              * @event groupdblclick\r
64271              * Fires when group header is double clicked. <b>Only applies for grids with a {@link Ext.grid.GroupingView GroupingView}</b>.\r
64272              * @param {Grid} this\r
64273              * @param {String} groupField\r
64274              * @param {String} groupValue\r
64275              * @param {Ext.EventObject} e\r
64276              */\r
64277             'groupdblclick',\r
64278             /**\r
64279              * @event containerclick\r
64280              * Fires when the container is clicked. The container consists of any part of the grid body that is not covered by a row.\r
64281              * @param {Grid} this\r
64282              * @param {Ext.EventObject} e\r
64283              */\r
64284             'containerclick',\r
64285             /**\r
64286              * @event containerdblclick\r
64287              * Fires when the container is double clicked. The container consists of any part of the grid body that is not covered by a row.\r
64288              * @param {Grid} this\r
64289              * @param {Ext.EventObject} e\r
64290              */\r
64291             'containerdblclick',\r
64292             \r
64293             /**\r
64294              * @event rowbodyclick\r
64295              * Fires when the row body is clicked. <b>Only applies for grids with {@link Ext.grid.GridView#enableRowBody enableRowBody} configured.</b>\r
64296              * @param {Grid} this\r
64297              * @param {Number} rowIndex\r
64298              * @param {Ext.EventObject} e\r
64299              */\r
64300             'rowbodyclick',\r
64301             /**\r
64302              * @event rowbodydblclick\r
64303              * Fires when the row body is double clicked. <b>Only applies for grids with {@link Ext.grid.GridView#enableRowBody enableRowBody} configured.</b>\r
64304              * @param {Grid} this\r
64305              * @param {Number} rowIndex\r
64306              * @param {Ext.EventObject} e\r
64307              */\r
64308             'rowbodydblclick',\r
64309             \r
64310             /**\r
64311              * @event rowcontextmenu\r
64312              * Fires when a row is right clicked\r
64313              * @param {Grid} this\r
64314              * @param {Number} rowIndex\r
64315              * @param {Ext.EventObject} e\r
64316              */\r
64317             'rowcontextmenu',\r
64318             /**\r
64319              * @event cellcontextmenu\r
64320              * Fires when a cell is right clicked\r
64321              * @param {Grid} this\r
64322              * @param {Number} rowIndex\r
64323              * @param {Number} cellIndex\r
64324              * @param {Ext.EventObject} e\r
64325              */\r
64326             'cellcontextmenu',\r
64327             /**\r
64328              * @event headercontextmenu\r
64329              * Fires when a header is right clicked\r
64330              * @param {Grid} this\r
64331              * @param {Number} columnIndex\r
64332              * @param {Ext.EventObject} e\r
64333              */\r
64334             'headercontextmenu',\r
64335             /**\r
64336              * @event groupcontextmenu\r
64337              * Fires when group header is right clicked. <b>Only applies for grids with a {@link Ext.grid.GroupingView GroupingView}</b>.\r
64338              * @param {Grid} this\r
64339              * @param {String} groupField\r
64340              * @param {String} groupValue\r
64341              * @param {Ext.EventObject} e\r
64342              */\r
64343             'groupcontextmenu',\r
64344             /**\r
64345              * @event containercontextmenu\r
64346              * Fires when the container is right clicked. The container consists of any part of the grid body that is not covered by a row.\r
64347              * @param {Grid} this\r
64348              * @param {Ext.EventObject} e\r
64349              */\r
64350             'containercontextmenu',\r
64351             /**\r
64352              * @event rowbodycontextmenu\r
64353              * Fires when the row body is right clicked. <b>Only applies for grids with {@link Ext.grid.GridView#enableRowBody enableRowBody} configured.</b>\r
64354              * @param {Grid} this\r
64355              * @param {Number} rowIndex\r
64356              * @param {Ext.EventObject} e\r
64357              */\r
64358             'rowbodycontextmenu',\r
64359             /**\r
64360              * @event bodyscroll\r
64361              * Fires when the body element is scrolled\r
64362              * @param {Number} scrollLeft\r
64363              * @param {Number} scrollTop\r
64364              */\r
64365             'bodyscroll',\r
64366             /**\r
64367              * @event columnresize\r
64368              * Fires when the user resizes a column\r
64369              * @param {Number} columnIndex\r
64370              * @param {Number} newSize\r
64371              */\r
64372             'columnresize',\r
64373             /**\r
64374              * @event columnmove\r
64375              * Fires when the user moves a column\r
64376              * @param {Number} oldIndex\r
64377              * @param {Number} newIndex\r
64378              */\r
64379             'columnmove',\r
64380             /**\r
64381              * @event sortchange\r
64382              * Fires when the grid's store sort changes\r
64383              * @param {Grid} this\r
64384              * @param {Object} sortInfo An object with the keys field and direction\r
64385              */\r
64386             'sortchange',\r
64387             /**\r
64388              * @event groupchange\r
64389              * Fires when the grid's grouping changes (only applies for grids with a {@link Ext.grid.GroupingView GroupingView})\r
64390              * @param {Grid} this\r
64391              * @param {String} groupField A string with the grouping field, null if the store is not grouped.\r
64392              */\r
64393             'groupchange',\r
64394             /**\r
64395              * @event reconfigure\r
64396              * Fires when the grid is reconfigured with a new store and/or column model.\r
64397              * @param {Grid} this\r
64398              * @param {Ext.data.Store} store The new store\r
64399              * @param {Ext.grid.ColumnModel} colModel The new column model\r
64400              */\r
64401             'reconfigure',\r
64402             /**\r
64403              * @event viewready\r
64404              * Fires when the grid view is available (use this for selecting a default row). \r
64405              * @param {Grid} this\r
64406              */\r
64407             'viewready'\r
64408         );\r
64409     },\r
64410 \r
64411     // private\r
64412     onRender : function(ct, position){\r
64413         Ext.grid.GridPanel.superclass.onRender.apply(this, arguments);\r
64414 \r
64415         var c = this.getGridEl();\r
64416 \r
64417         this.el.addClass('x-grid-panel');\r
64418 \r
64419         this.mon(c, {\r
64420             scope: this,\r
64421             mousedown: this.onMouseDown,\r
64422             click: this.onClick,\r
64423             dblclick: this.onDblClick,\r
64424             contextmenu: this.onContextMenu\r
64425         });\r
64426 \r
64427         this.relayEvents(c, ['mousedown','mouseup','mouseover','mouseout','keypress', 'keydown']);\r
64428 \r
64429         var view = this.getView();\r
64430         view.init(this);\r
64431         view.render();\r
64432         this.getSelectionModel().init(this);\r
64433     },\r
64434 \r
64435     // private\r
64436     initEvents : function(){\r
64437         Ext.grid.GridPanel.superclass.initEvents.call(this);\r
64438 \r
64439         if(this.loadMask){\r
64440             this.loadMask = new Ext.LoadMask(this.bwrap,\r
64441                     Ext.apply({store:this.store}, this.loadMask));\r
64442         }\r
64443     },\r
64444 \r
64445     initStateEvents : function(){\r
64446         Ext.grid.GridPanel.superclass.initStateEvents.call(this);\r
64447         this.mon(this.colModel, 'hiddenchange', this.saveState, this, {delay: 100});\r
64448     },\r
64449 \r
64450     applyState : function(state){\r
64451         var cm = this.colModel,\r
64452             cs = state.columns,\r
64453             store = this.store,\r
64454             s,\r
64455             c,\r
64456             oldIndex;\r
64457             \r
64458         if(cs){\r
64459             for(var i = 0, len = cs.length; i < len; i++){\r
64460                 s = cs[i];\r
64461                 c = cm.getColumnById(s.id);\r
64462                 if(c){\r
64463                     c.hidden = s.hidden;\r
64464                     c.width = s.width;\r
64465                     oldIndex = cm.getIndexById(s.id);\r
64466                     if(oldIndex != i){\r
64467                         cm.moveColumn(oldIndex, i);\r
64468                     }\r
64469                 }\r
64470             }\r
64471         }\r
64472         if(store){\r
64473             s = state.sort;\r
64474             if(s){\r
64475                 store[store.remoteSort ? 'setDefaultSort' : 'sort'](s.field, s.direction);\r
64476             }\r
64477             s = state.group;\r
64478             if(store.groupBy){\r
64479                 if(s){\r
64480                     store.groupBy(s);\r
64481                 }else{\r
64482                     store.clearGrouping();\r
64483                 }\r
64484             }\r
64485 \r
64486         }\r
64487         var o = Ext.apply({}, state);\r
64488         delete o.columns;\r
64489         delete o.sort;\r
64490         Ext.grid.GridPanel.superclass.applyState.call(this, o);\r
64491     },\r
64492 \r
64493     getState : function(){\r
64494         var o = {columns: []},\r
64495             store = this.store,\r
64496             ss,\r
64497             gs;\r
64498             \r
64499         for(var i = 0, c; (c = this.colModel.config[i]); i++){\r
64500             o.columns[i] = {\r
64501                 id: c.id,\r
64502                 width: c.width\r
64503             };\r
64504             if(c.hidden){\r
64505                 o.columns[i].hidden = true;\r
64506             }\r
64507         }\r
64508         if(store){\r
64509             ss = store.getSortState();\r
64510             if(ss){\r
64511                 o.sort = ss;\r
64512             }\r
64513             if(store.getGroupState){\r
64514                 gs = store.getGroupState();\r
64515                 if(gs){\r
64516                     o.group = gs;\r
64517                 }\r
64518             }\r
64519         }\r
64520         return o;\r
64521     },\r
64522 \r
64523     // private\r
64524     afterRender : function(){\r
64525         Ext.grid.GridPanel.superclass.afterRender.call(this);\r
64526         var v = this.view;\r
64527         this.on('bodyresize', v.layout, v);\r
64528         v.layout();\r
64529         if(this.deferRowRender){\r
64530             v.afterRender.defer(10, this.view);\r
64531         }else{\r
64532             v.afterRender();\r
64533         }\r
64534         this.viewReady = true;\r
64535     },\r
64536 \r
64537     /**\r
64538      * <p>Reconfigures the grid to use a different Store and Column Model\r
64539      * and fires the 'reconfigure' event. The View will be bound to the new\r
64540      * objects and refreshed.</p>\r
64541      * <p>Be aware that upon reconfiguring a GridPanel, certain existing settings <i>may</i> become\r
64542      * invalidated. For example the configured {@link #autoExpandColumn} may no longer exist in the\r
64543      * new ColumnModel. Also, an existing {@link Ext.PagingToolbar PagingToolbar} will still be bound\r
64544      * to the old Store, and will need rebinding. Any {@link #plugins} might also need reconfiguring\r
64545      * with the new data.</p>\r
64546      * @param {Ext.data.Store} store The new {@link Ext.data.Store} object\r
64547      * @param {Ext.grid.ColumnModel} colModel The new {@link Ext.grid.ColumnModel} object\r
64548      */\r
64549     reconfigure : function(store, colModel){\r
64550         var rendered = this.rendered;\r
64551         if(rendered){\r
64552             if(this.loadMask){\r
64553                 this.loadMask.destroy();\r
64554                 this.loadMask = new Ext.LoadMask(this.bwrap,\r
64555                         Ext.apply({}, {store:store}, this.initialConfig.loadMask));\r
64556             }\r
64557         }\r
64558         if(this.view){\r
64559             this.view.initData(store, colModel);\r
64560         }\r
64561         this.store = store;\r
64562         this.colModel = colModel;\r
64563         if(rendered){\r
64564             this.view.refresh(true);\r
64565         }\r
64566         this.fireEvent('reconfigure', this, store, colModel);\r
64567     },\r
64568 \r
64569     // private\r
64570     onDestroy : function(){\r
64571         if(this.rendered){\r
64572             Ext.destroy(this.view, this.loadMask);\r
64573         }else if(this.store && this.store.autoDestroy){\r
64574             this.store.destroy();\r
64575         }\r
64576         Ext.destroy(this.colModel, this.selModel);\r
64577         this.store = this.selModel = this.colModel = this.view = this.loadMask = null;\r
64578         Ext.grid.GridPanel.superclass.onDestroy.call(this);\r
64579     },\r
64580 \r
64581     // private\r
64582     processEvent : function(name, e){\r
64583         this.fireEvent(name, e);\r
64584         var t = e.getTarget(),\r
64585             v = this.view,\r
64586             header = v.findHeaderIndex(t);\r
64587             \r
64588         if(header !== false){\r
64589             this.fireEvent('header' + name, this, header, e);\r
64590         }else{\r
64591             var row = v.findRowIndex(t),\r
64592                 cell,\r
64593                 body;\r
64594             if(row !== false){\r
64595                 this.fireEvent('row' + name, this, row, e);\r
64596                 cell = v.findCellIndex(t);\r
64597                 body = v.findRowBody(t);\r
64598                 if(cell !== false){\r
64599                     this.fireEvent('cell' + name, this, row, cell, e);\r
64600                 }\r
64601                 if(body){\r
64602                     this.fireEvent('rowbody' + name, this, row, e);\r
64603                 }\r
64604             }else{\r
64605                 this.fireEvent('container' + name, this, e);\r
64606             }\r
64607         }\r
64608         this.view.processEvent(name, e);\r
64609     },\r
64610 \r
64611     // private\r
64612     onClick : function(e){\r
64613         this.processEvent('click', e);\r
64614     },\r
64615 \r
64616     // private\r
64617     onMouseDown : function(e){\r
64618         this.processEvent('mousedown', e);\r
64619     },\r
64620 \r
64621     // private\r
64622     onContextMenu : function(e, t){\r
64623         this.processEvent('contextmenu', e);\r
64624     },\r
64625 \r
64626     // private\r
64627     onDblClick : function(e){\r
64628         this.processEvent('dblclick', e);\r
64629     },\r
64630 \r
64631     // private\r
64632     walkCells : function(row, col, step, fn, scope){\r
64633         var cm = this.colModel, \r
64634             clen = cm.getColumnCount(),\r
64635             ds = this.store, \r
64636             rlen = ds.getCount(), \r
64637             first = true;\r
64638         if(step < 0){\r
64639             if(col < 0){\r
64640                 row--;\r
64641                 first = false;\r
64642             }\r
64643             while(row >= 0){\r
64644                 if(!first){\r
64645                     col = clen-1;\r
64646                 }\r
64647                 first = false;\r
64648                 while(col >= 0){\r
64649                     if(fn.call(scope || this, row, col, cm) === true){\r
64650                         return [row, col];\r
64651                     }\r
64652                     col--;\r
64653                 }\r
64654                 row--;\r
64655             }\r
64656         } else {\r
64657             if(col >= clen){\r
64658                 row++;\r
64659                 first = false;\r
64660             }\r
64661             while(row < rlen){\r
64662                 if(!first){\r
64663                     col = 0;\r
64664                 }\r
64665                 first = false;\r
64666                 while(col < clen){\r
64667                     if(fn.call(scope || this, row, col, cm) === true){\r
64668                         return [row, col];\r
64669                     }\r
64670                     col++;\r
64671                 }\r
64672                 row++;\r
64673             }\r
64674         }\r
64675         return null;\r
64676     },\r
64677 \r
64678     // private\r
64679     onResize : function(){\r
64680         Ext.grid.GridPanel.superclass.onResize.apply(this, arguments);\r
64681         if(this.viewReady){\r
64682             this.view.layout();\r
64683         }\r
64684     },\r
64685 \r
64686     /**\r
64687      * Returns the grid's underlying element.\r
64688      * @return {Element} The element\r
64689      */\r
64690     getGridEl : function(){\r
64691         return this.body;\r
64692     },\r
64693 \r
64694     // private for compatibility, overridden by editor grid\r
64695     stopEditing : Ext.emptyFn,\r
64696 \r
64697     /**\r
64698      * Returns the grid's selection model configured by the <code>{@link #selModel}</code>\r
64699      * configuration option. If no selection model was configured, this will create\r
64700      * and return a {@link Ext.grid.RowSelectionModel RowSelectionModel}.\r
64701      * @return {SelectionModel}\r
64702      */\r
64703     getSelectionModel : function(){\r
64704         if(!this.selModel){\r
64705             this.selModel = new Ext.grid.RowSelectionModel(\r
64706                     this.disableSelection ? {selectRow: Ext.emptyFn} : null);\r
64707         }\r
64708         return this.selModel;\r
64709     },\r
64710 \r
64711     /**\r
64712      * Returns the grid's data store.\r
64713      * @return {Ext.data.Store} The store\r
64714      */\r
64715     getStore : function(){\r
64716         return this.store;\r
64717     },\r
64718 \r
64719     /**\r
64720      * Returns the grid's ColumnModel.\r
64721      * @return {Ext.grid.ColumnModel} The column model\r
64722      */\r
64723     getColumnModel : function(){\r
64724         return this.colModel;\r
64725     },\r
64726 \r
64727     /**\r
64728      * Returns the grid's GridView object.\r
64729      * @return {Ext.grid.GridView} The grid view\r
64730      */\r
64731     getView : function(){\r
64732         if(!this.view){\r
64733             this.view = new Ext.grid.GridView(this.viewConfig);\r
64734         }\r
64735         return this.view;\r
64736     },\r
64737     /**\r
64738      * Called to get grid's drag proxy text, by default returns this.ddText.\r
64739      * @return {String} The text\r
64740      */\r
64741     getDragDropText : function(){\r
64742         var count = this.selModel.getCount();\r
64743         return String.format(this.ddText, count, count == 1 ? '' : 's');\r
64744     }\r
64745 \r
64746     /** \r
64747      * @cfg {String/Number} activeItem \r
64748      * @hide \r
64749      */\r
64750     /** \r
64751      * @cfg {Boolean} autoDestroy \r
64752      * @hide \r
64753      */\r
64754     /** \r
64755      * @cfg {Object/String/Function} autoLoad \r
64756      * @hide \r
64757      */\r
64758     /** \r
64759      * @cfg {Boolean} autoWidth \r
64760      * @hide \r
64761      */\r
64762     /** \r
64763      * @cfg {Boolean/Number} bufferResize \r
64764      * @hide \r
64765      */\r
64766     /** \r
64767      * @cfg {String} defaultType \r
64768      * @hide \r
64769      */\r
64770     /** \r
64771      * @cfg {Object} defaults \r
64772      * @hide \r
64773      */\r
64774     /** \r
64775      * @cfg {Boolean} hideBorders \r
64776      * @hide \r
64777      */\r
64778     /** \r
64779      * @cfg {Mixed} items \r
64780      * @hide \r
64781      */\r
64782     /** \r
64783      * @cfg {String} layout \r
64784      * @hide \r
64785      */\r
64786     /** \r
64787      * @cfg {Object} layoutConfig \r
64788      * @hide \r
64789      */\r
64790     /** \r
64791      * @cfg {Boolean} monitorResize \r
64792      * @hide \r
64793      */\r
64794     /** \r
64795      * @property items \r
64796      * @hide \r
64797      */\r
64798     /** \r
64799      * @method add \r
64800      * @hide \r
64801      */\r
64802     /** \r
64803      * @method cascade \r
64804      * @hide \r
64805      */\r
64806     /** \r
64807      * @method doLayout \r
64808      * @hide \r
64809      */\r
64810     /** \r
64811      * @method find \r
64812      * @hide \r
64813      */\r
64814     /** \r
64815      * @method findBy \r
64816      * @hide \r
64817      */\r
64818     /** \r
64819      * @method findById \r
64820      * @hide \r
64821      */\r
64822     /** \r
64823      * @method findByType \r
64824      * @hide \r
64825      */\r
64826     /** \r
64827      * @method getComponent \r
64828      * @hide \r
64829      */\r
64830     /** \r
64831      * @method getLayout \r
64832      * @hide \r
64833      */\r
64834     /** \r
64835      * @method getUpdater \r
64836      * @hide \r
64837      */\r
64838     /** \r
64839      * @method insert \r
64840      * @hide \r
64841      */\r
64842     /** \r
64843      * @method load \r
64844      * @hide \r
64845      */\r
64846     /** \r
64847      * @method remove \r
64848      * @hide \r
64849      */\r
64850     /** \r
64851      * @event add \r
64852      * @hide \r
64853      */\r
64854     /** \r
64855      * @event afterlayout \r
64856      * @hide \r
64857      */\r
64858     /** \r
64859      * @event beforeadd \r
64860      * @hide \r
64861      */\r
64862     /** \r
64863      * @event beforeremove \r
64864      * @hide \r
64865      */\r
64866     /** \r
64867      * @event remove \r
64868      * @hide \r
64869      */\r
64870 \r
64871 \r
64872 \r
64873     /**\r
64874      * @cfg {String} allowDomMove  @hide\r
64875      */\r
64876     /**\r
64877      * @cfg {String} autoEl @hide\r
64878      */\r
64879     /**\r
64880      * @cfg {String} applyTo  @hide\r
64881      */\r
64882     /**\r
64883      * @cfg {String} autoScroll  @hide\r
64884      */\r
64885     /**\r
64886      * @cfg {String} bodyBorder  @hide\r
64887      */\r
64888     /**\r
64889      * @cfg {String} bodyStyle  @hide\r
64890      */\r
64891     /**\r
64892      * @cfg {String} contentEl  @hide\r
64893      */\r
64894     /**\r
64895      * @cfg {String} disabledClass  @hide\r
64896      */\r
64897     /**\r
64898      * @cfg {String} elements  @hide\r
64899      */\r
64900     /**\r
64901      * @cfg {String} html  @hide\r
64902      */\r
64903     /**\r
64904      * @cfg {Boolean} preventBodyReset\r
64905      * @hide\r
64906      */\r
64907     /**\r
64908      * @property disabled\r
64909      * @hide\r
64910      */\r
64911     /**\r
64912      * @method applyToMarkup\r
64913      * @hide\r
64914      */\r
64915     /**\r
64916      * @method enable\r
64917      * @hide\r
64918      */\r
64919     /**\r
64920      * @method disable\r
64921      * @hide\r
64922      */\r
64923     /**\r
64924      * @method setDisabled\r
64925      * @hide\r
64926      */\r
64927 });\r
64928 Ext.reg('grid', Ext.grid.GridPanel);/**
64929  * @class Ext.grid.GridView
64930  * @extends Ext.util.Observable
64931  * <p>This class encapsulates the user interface of an {@link Ext.grid.GridPanel}.
64932  * Methods of this class may be used to access user interface elements to enable
64933  * special display effects. Do not change the DOM structure of the user interface.</p>
64934  * <p>This class does not provide ways to manipulate the underlying data. The data
64935  * model of a Grid is held in an {@link Ext.data.Store}.</p>
64936  * @constructor
64937  * @param {Object} config
64938  */
64939 Ext.grid.GridView = Ext.extend(Ext.util.Observable, {
64940     /**
64941      * Override this function to apply custom CSS classes to rows during rendering.  You can also supply custom
64942      * parameters to the row template for the current row to customize how it is rendered using the <b>rowParams</b>
64943      * parameter.  This function should return the CSS class name (or empty string '' for none) that will be added
64944      * to the row's wrapping div.  To apply multiple class names, simply return them space-delimited within the string
64945      * (e.g., 'my-class another-class'). Example usage:
64946     <pre><code>
64947 viewConfig: {
64948     forceFit: true,
64949     showPreview: true, // custom property
64950     enableRowBody: true, // required to create a second, full-width row to show expanded Record data
64951     getRowClass: function(record, rowIndex, rp, ds){ // rp = rowParams
64952         if(this.showPreview){
64953             rp.body = '&lt;p>'+record.data.excerpt+'&lt;/p>';
64954             return 'x-grid3-row-expanded';
64955         }
64956         return 'x-grid3-row-collapsed';
64957     }
64958 },     
64959     </code></pre>
64960      * @param {Record} record The {@link Ext.data.Record} corresponding to the current row.
64961      * @param {Number} index The row index.
64962      * @param {Object} rowParams A config object that is passed to the row template during rendering that allows
64963      * customization of various aspects of a grid row.
64964      * <p>If {@link #enableRowBody} is configured <b><tt></tt>true</b>, then the following properties may be set
64965      * by this function, and will be used to render a full-width expansion row below each grid row:</p>
64966      * <ul>
64967      * <li><code>body</code> : String <div class="sub-desc">An HTML fragment to be used as the expansion row's body content (defaults to '').</div></li>
64968      * <li><code>bodyStyle</code> : String <div class="sub-desc">A CSS style specification that will be applied to the expansion row's &lt;tr> element. (defaults to '').</div></li>
64969      * </ul>
64970      * The following property will be passed in, and may be appended to:
64971      * <ul>
64972      * <li><code>tstyle</code> : String <div class="sub-desc">A CSS style specification that willl be applied to the &lt;table> element which encapsulates
64973      * both the standard grid row, and any expansion row.</div></li>
64974      * </ul>
64975      * @param {Store} store The {@link Ext.data.Store} this grid is bound to
64976      * @method getRowClass
64977      * @return {String} a CSS class name to add to the row.
64978      */
64979     /**
64980      * @cfg {Boolean} enableRowBody True to add a second TR element per row that can be used to provide a row body
64981      * that spans beneath the data row.  Use the {@link #getRowClass} method's rowParams config to customize the row body.
64982      */
64983     /**
64984      * @cfg {String} emptyText Default text (html tags are accepted) to display in the grid body when no rows
64985      * are available (defaults to ''). This value will be used to update the <tt>{@link #mainBody}</tt>:
64986     <pre><code>
64987     this.mainBody.update('&lt;div class="x-grid-empty">' + this.emptyText + '&lt;/div>');
64988     </code></pre>
64989      */
64990     /**
64991      * @cfg {Boolean} headersDisabled True to disable the grid column headers (defaults to <tt>false</tt>). 
64992      * Use the {@link Ext.grid.ColumnModel ColumnModel} <tt>{@link Ext.grid.ColumnModel#menuDisabled menuDisabled}</tt>
64993      * config to disable the <i>menu</i> for individual columns.  While this config is true the
64994      * following will be disabled:<div class="mdetail-params"><ul>
64995      * <li>clicking on header to sort</li>
64996      * <li>the trigger to reveal the menu.</li>
64997      * </ul></div>
64998      */
64999     /**
65000      * <p>A customized implementation of a {@link Ext.dd.DragZone DragZone} which provides default implementations
65001      * of the template methods of DragZone to enable dragging of the selected rows of a GridPanel.
65002      * See {@link Ext.grid.GridDragZone} for details.</p>
65003      * <p>This will <b>only</b> be present:<div class="mdetail-params"><ul>
65004      * <li><i>if</i> the owning GridPanel was configured with {@link Ext.grid.GridPanel#enableDragDrop enableDragDrop}: <tt>true</tt>.</li>
65005      * <li><i>after</i> the owning GridPanel has been rendered.</li>
65006      * </ul></div>
65007      * @property dragZone
65008      * @type {Ext.grid.GridDragZone}
65009      */
65010     /**
65011      * @cfg {Boolean} deferEmptyText True to defer <tt>{@link #emptyText}</tt> being applied until the store's
65012      * first load (defaults to <tt>true</tt>).
65013      */
65014     deferEmptyText : true,
65015     /**
65016      * @cfg {Number} scrollOffset The amount of space to reserve for the vertical scrollbar
65017      * (defaults to <tt>undefined</tt>). If an explicit value isn't specified, this will be automatically
65018      * calculated.
65019      */
65020     scrollOffset : undefined,
65021     /**
65022      * @cfg {Boolean} autoFill
65023      * Defaults to <tt>false</tt>.  Specify <tt>true</tt> to have the column widths re-proportioned
65024      * when the grid is <b>initially rendered</b>.  The 
65025      * {@link Ext.grid.Column#width initially configured width}</tt> of each column will be adjusted
65026      * to fit the grid width and prevent horizontal scrolling. If columns are later resized (manually
65027      * or programmatically), the other columns in the grid will <b>not</b> be resized to fit the grid width.
65028      * See <tt>{@link #forceFit}</tt> also.
65029      */
65030     autoFill : false,
65031     /**
65032      * @cfg {Boolean} forceFit
65033      * Defaults to <tt>false</tt>.  Specify <tt>true</tt> to have the column widths re-proportioned
65034      * at <b>all times</b>.  The {@link Ext.grid.Column#width initially configured width}</tt> of each
65035      * column will be adjusted to fit the grid width and prevent horizontal scrolling. If columns are
65036      * later resized (manually or programmatically), the other columns in the grid <b>will</b> be resized
65037      * to fit the grid width. See <tt>{@link #autoFill}</tt> also.
65038      */
65039     forceFit : false,
65040     /**
65041      * @cfg {Array} sortClasses The CSS classes applied to a header when it is sorted. (defaults to <tt>['sort-asc', 'sort-desc']</tt>)
65042      */
65043     sortClasses : ['sort-asc', 'sort-desc'],
65044     /**
65045      * @cfg {String} sortAscText The text displayed in the 'Sort Ascending' menu item (defaults to <tt>'Sort Ascending'</tt>)
65046      */
65047     sortAscText : 'Sort Ascending',
65048     /**
65049      * @cfg {String} sortDescText The text displayed in the 'Sort Descending' menu item (defaults to <tt>'Sort Descending'</tt>)
65050      */
65051     sortDescText : 'Sort Descending',
65052     /**
65053      * @cfg {String} columnsText The text displayed in the 'Columns' menu item (defaults to <tt>'Columns'</tt>)
65054      */
65055     columnsText : 'Columns',
65056
65057     /**
65058      * @cfg {String} selectedRowClass The CSS class applied to a selected row (defaults to <tt>'x-grid3-row-selected'</tt>). An
65059      * example overriding the default styling:
65060     <pre><code>
65061     .x-grid3-row-selected {background-color: yellow;}
65062     </code></pre>
65063      * Note that this only controls the row, and will not do anything for the text inside it.  To style inner
65064      * facets (like text) use something like:
65065     <pre><code>
65066     .x-grid3-row-selected .x-grid3-cell-inner {
65067         color: #FFCC00;
65068     }
65069     </code></pre>
65070      * @type String
65071      */
65072     selectedRowClass : 'x-grid3-row-selected',
65073
65074     // private
65075     borderWidth : 2,
65076     tdClass : 'x-grid3-cell',
65077     hdCls : 'x-grid3-hd',
65078     markDirty : true,
65079
65080     /**
65081      * @cfg {Number} cellSelectorDepth The number of levels to search for cells in event delegation (defaults to <tt>4</tt>)
65082      */
65083     cellSelectorDepth : 4,
65084     /**
65085      * @cfg {Number} rowSelectorDepth The number of levels to search for rows in event delegation (defaults to <tt>10</tt>)
65086      */
65087     rowSelectorDepth : 10,
65088     
65089     /**
65090      * @cfg {Number} rowBodySelectorDepth The number of levels to search for row bodies in event delegation (defaults to <tt>10</tt>)
65091      */
65092     rowBodySelectorDepth : 10,
65093
65094     /**
65095      * @cfg {String} cellSelector The selector used to find cells internally (defaults to <tt>'td.x-grid3-cell'</tt>)
65096      */
65097     cellSelector : 'td.x-grid3-cell',
65098     /**
65099      * @cfg {String} rowSelector The selector used to find rows internally (defaults to <tt>'div.x-grid3-row'</tt>)
65100      */
65101     rowSelector : 'div.x-grid3-row',
65102     
65103     /**
65104      * @cfg {String} rowBodySelector The selector used to find row bodies internally (defaults to <tt>'div.x-grid3-row'</tt>)
65105      */
65106     rowBodySelector : 'div.x-grid3-row-body',
65107     
65108     // private
65109     firstRowCls: 'x-grid3-row-first',
65110     lastRowCls: 'x-grid3-row-last',
65111     rowClsRe: /(?:^|\s+)x-grid3-row-(first|last|alt)(?:\s+|$)/g,
65112     
65113     constructor : function(config){
65114         Ext.apply(this, config);
65115             // These events are only used internally by the grid components
65116             this.addEvents(
65117                 /**
65118                  * @event beforerowremoved
65119                  * Internal UI Event. Fired before a row is removed.
65120                  * @param {Ext.grid.GridView} view
65121                  * @param {Number} rowIndex The index of the row to be removed.
65122                  * @param {Ext.data.Record} record The Record to be removed
65123                  */
65124                 'beforerowremoved',
65125                 /**
65126                  * @event beforerowsinserted
65127                  * Internal UI Event. Fired before rows are inserted.
65128                  * @param {Ext.grid.GridView} view
65129                  * @param {Number} firstRow The index of the first row to be inserted.
65130                  * @param {Number} lastRow The index of the last row to be inserted.
65131                  */
65132                 'beforerowsinserted',
65133                 /**
65134                  * @event beforerefresh
65135                  * Internal UI Event. Fired before the view is refreshed.
65136                  * @param {Ext.grid.GridView} view
65137                  */
65138                 'beforerefresh',
65139                 /**
65140                  * @event rowremoved
65141                  * Internal UI Event. Fired after a row is removed.
65142                  * @param {Ext.grid.GridView} view
65143                  * @param {Number} rowIndex The index of the row that was removed.
65144                  * @param {Ext.data.Record} record The Record that was removed
65145                  */
65146                 'rowremoved',
65147                 /**
65148                  * @event rowsinserted
65149                  * Internal UI Event. Fired after rows are inserted.
65150                  * @param {Ext.grid.GridView} view
65151                  * @param {Number} firstRow The index of the first inserted.
65152                  * @param {Number} lastRow The index of the last row inserted.
65153                  */
65154                 'rowsinserted',
65155                 /**
65156                  * @event rowupdated
65157                  * Internal UI Event. Fired after a row has been updated.
65158                  * @param {Ext.grid.GridView} view
65159                  * @param {Number} firstRow The index of the row updated.
65160                  * @param {Ext.data.record} record The Record backing the row updated.
65161                  */
65162                 'rowupdated',
65163                 /**
65164                  * @event refresh
65165                  * Internal UI Event. Fired after the GridView's body has been refreshed.
65166                  * @param {Ext.grid.GridView} view
65167                  */
65168                 'refresh'
65169             );
65170             Ext.grid.GridView.superclass.constructor.call(this);    
65171     },
65172
65173     /* -------------------------------- UI Specific ----------------------------- */
65174
65175     // private
65176     initTemplates : function(){
65177         var ts = this.templates || {};
65178         if(!ts.master){
65179             ts.master = new Ext.Template(
65180                     '<div class="x-grid3" hidefocus="true">',
65181                         '<div class="x-grid3-viewport">',
65182                             '<div class="x-grid3-header"><div class="x-grid3-header-inner"><div class="x-grid3-header-offset" style="{ostyle}">{header}</div></div><div class="x-clear"></div></div>',
65183                             '<div class="x-grid3-scroller"><div class="x-grid3-body" style="{bstyle}">{body}</div><a href="#" class="x-grid3-focus" tabIndex="-1"></a></div>',
65184                         '</div>',
65185                         '<div class="x-grid3-resize-marker">&#160;</div>',
65186                         '<div class="x-grid3-resize-proxy">&#160;</div>',
65187                     '</div>'
65188                     );
65189         }
65190
65191         if(!ts.header){
65192             ts.header = new Ext.Template(
65193                     '<table border="0" cellspacing="0" cellpadding="0" style="{tstyle}">',
65194                     '<thead><tr class="x-grid3-hd-row">{cells}</tr></thead>',
65195                     '</table>'
65196                     );
65197         }
65198
65199         if(!ts.hcell){
65200             ts.hcell = new Ext.Template(
65201                     '<td class="x-grid3-hd x-grid3-cell x-grid3-td-{id} {css}" style="{style}"><div {tooltip} {attr} class="x-grid3-hd-inner x-grid3-hd-{id}" unselectable="on" style="{istyle}">', this.grid.enableHdMenu ? '<a class="x-grid3-hd-btn" href="#"></a>' : '',
65202                     '{value}<img class="x-grid3-sort-icon" src="', Ext.BLANK_IMAGE_URL, '" />',
65203                     '</div></td>'
65204                     );
65205         }
65206
65207         if(!ts.body){
65208             ts.body = new Ext.Template('{rows}');
65209         }
65210
65211         if(!ts.row){
65212             ts.row = new Ext.Template(
65213                     '<div class="x-grid3-row {alt}" style="{tstyle}"><table class="x-grid3-row-table" border="0" cellspacing="0" cellpadding="0" style="{tstyle}">',
65214                     '<tbody><tr>{cells}</tr>',
65215                     (this.enableRowBody ? '<tr class="x-grid3-row-body-tr" style="{bodyStyle}"><td colspan="{cols}" class="x-grid3-body-cell" tabIndex="0" hidefocus="on"><div class="x-grid3-row-body">{body}</div></td></tr>' : ''),
65216                     '</tbody></table></div>'
65217                     );
65218         }
65219
65220         if(!ts.cell){
65221             ts.cell = new Ext.Template(
65222                     '<td class="x-grid3-col x-grid3-cell x-grid3-td-{id} {css}" style="{style}" tabIndex="0" {cellAttr}>',
65223                     '<div class="x-grid3-cell-inner x-grid3-col-{id}" unselectable="on" {attr}>{value}</div>',
65224                     '</td>'
65225                     );
65226         }
65227
65228         for(var k in ts){
65229             var t = ts[k];
65230             if(t && Ext.isFunction(t.compile) && !t.compiled){
65231                 t.disableFormats = true;
65232                 t.compile();
65233             }
65234         }
65235
65236         this.templates = ts;
65237         this.colRe = new RegExp('x-grid3-td-([^\\s]+)', '');
65238     },
65239
65240     // private
65241     fly : function(el){
65242         if(!this._flyweight){
65243             this._flyweight = new Ext.Element.Flyweight(document.body);
65244         }
65245         this._flyweight.dom = el;
65246         return this._flyweight;
65247     },
65248
65249     // private
65250     getEditorParent : function(){
65251         return this.scroller.dom;
65252     },
65253
65254     // private
65255     initElements : function(){
65256         var E = Ext.Element;
65257
65258         var el = this.grid.getGridEl().dom.firstChild;
65259         var cs = el.childNodes;
65260
65261         this.el = new E(el);
65262
65263         this.mainWrap = new E(cs[0]);
65264         this.mainHd = new E(this.mainWrap.dom.firstChild);
65265
65266         if(this.grid.hideHeaders){
65267             this.mainHd.setDisplayed(false);
65268         }
65269
65270         this.innerHd = this.mainHd.dom.firstChild;
65271         this.scroller = new E(this.mainWrap.dom.childNodes[1]);
65272         if(this.forceFit){
65273             this.scroller.setStyle('overflow-x', 'hidden');
65274         }
65275         /**
65276          * <i>Read-only</i>. The GridView's body Element which encapsulates all rows in the Grid.
65277          * This {@link Ext.Element Element} is only available after the GridPanel has been rendered.
65278          * @type Ext.Element
65279          * @property mainBody
65280          */
65281         this.mainBody = new E(this.scroller.dom.firstChild);
65282
65283         this.focusEl = new E(this.scroller.dom.childNodes[1]);
65284         this.focusEl.swallowEvent('click', true);
65285
65286         this.resizeMarker = new E(cs[1]);
65287         this.resizeProxy = new E(cs[2]);
65288     },
65289
65290     // private
65291     getRows : function(){
65292         return this.hasRows() ? this.mainBody.dom.childNodes : [];
65293     },
65294
65295     // finder methods, used with delegation
65296
65297     // private
65298     findCell : function(el){
65299         if(!el){
65300             return false;
65301         }
65302         return this.fly(el).findParent(this.cellSelector, this.cellSelectorDepth);
65303     },
65304
65305     /**
65306      * <p>Return the index of the grid column which contains the passed HTMLElement.</p>
65307      * See also {@link #findRowIndex}
65308      * @param {HTMLElement} el The target element
65309      * @return {Number} The column index, or <b>false</b> if the target element is not within a row of this GridView.
65310      */
65311     findCellIndex : function(el, requiredCls){
65312         var cell = this.findCell(el);
65313         if(cell && (!requiredCls || this.fly(cell).hasClass(requiredCls))){
65314             return this.getCellIndex(cell);
65315         }
65316         return false;
65317     },
65318
65319     // private
65320     getCellIndex : function(el){
65321         if(el){
65322             var m = el.className.match(this.colRe);
65323             if(m && m[1]){
65324                 return this.cm.getIndexById(m[1]);
65325             }
65326         }
65327         return false;
65328     },
65329
65330     // private
65331     findHeaderCell : function(el){
65332         var cell = this.findCell(el);
65333         return cell && this.fly(cell).hasClass(this.hdCls) ? cell : null;
65334     },
65335
65336     // private
65337     findHeaderIndex : function(el){
65338         return this.findCellIndex(el, this.hdCls);
65339     },
65340
65341     /**
65342      * Return the HtmlElement representing the grid row which contains the passed element.
65343      * @param {HTMLElement} el The target HTMLElement
65344      * @return {HTMLElement} The row element, or null if the target element is not within a row of this GridView.
65345      */
65346     findRow : function(el){
65347         if(!el){
65348             return false;
65349         }
65350         return this.fly(el).findParent(this.rowSelector, this.rowSelectorDepth);
65351     },
65352
65353     /**
65354      * <p>Return the index of the grid row which contains the passed HTMLElement.</p>
65355      * See also {@link #findCellIndex}
65356      * @param {HTMLElement} el The target HTMLElement
65357      * @return {Number} The row index, or <b>false</b> if the target element is not within a row of this GridView.
65358      */
65359     findRowIndex : function(el){
65360         var r = this.findRow(el);
65361         return r ? r.rowIndex : false;
65362     },
65363     
65364     /**
65365      * Return the HtmlElement representing the grid row body which contains the passed element.
65366      * @param {HTMLElement} el The target HTMLElement
65367      * @return {HTMLElement} The row body element, or null if the target element is not within a row body of this GridView.
65368      */
65369     findRowBody : function(el){
65370         if(!el){
65371             return false;
65372         }
65373         return this.fly(el).findParent(this.rowBodySelector, this.rowBodySelectorDepth);
65374     },
65375
65376     // getter methods for fetching elements dynamically in the grid
65377
65378     /**
65379      * Return the <tt>&lt;div></tt> HtmlElement which represents a Grid row for the specified index.
65380      * @param {Number} index The row index
65381      * @return {HtmlElement} The div element.
65382      */
65383     getRow : function(row){
65384         return this.getRows()[row];
65385     },
65386
65387     /**
65388      * Returns the grid's <tt>&lt;td></tt> HtmlElement at the specified coordinates.
65389      * @param {Number} row The row index in which to find the cell.
65390      * @param {Number} col The column index of the cell.
65391      * @return {HtmlElement} The td at the specified coordinates.
65392      */
65393     getCell : function(row, col){
65394         return this.getRow(row).getElementsByTagName('td')[col];
65395     },
65396
65397     /**
65398      * Return the <tt>&lt;td></tt> HtmlElement which represents the Grid's header cell for the specified column index.
65399      * @param {Number} index The column index
65400      * @return {HtmlElement} The td element.
65401      */
65402     getHeaderCell : function(index){
65403       return this.mainHd.dom.getElementsByTagName('td')[index];
65404     },
65405
65406     // manipulating elements
65407
65408     // private - use getRowClass to apply custom row classes
65409     addRowClass : function(row, cls){
65410         var r = this.getRow(row);
65411         if(r){
65412             this.fly(r).addClass(cls);
65413         }
65414     },
65415
65416     // private
65417     removeRowClass : function(row, cls){
65418         var r = this.getRow(row);
65419         if(r){
65420             this.fly(r).removeClass(cls);
65421         }
65422     },
65423
65424     // private
65425     removeRow : function(row){
65426         Ext.removeNode(this.getRow(row));
65427         this.syncFocusEl(row);
65428     },
65429     
65430     // private
65431     removeRows : function(firstRow, lastRow){
65432         var bd = this.mainBody.dom;
65433         for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
65434             Ext.removeNode(bd.childNodes[firstRow]);
65435         }
65436         this.syncFocusEl(firstRow);
65437     },
65438
65439     // scrolling stuff
65440
65441     // private
65442     getScrollState : function(){
65443         var sb = this.scroller.dom;
65444         return {left: sb.scrollLeft, top: sb.scrollTop};
65445     },
65446
65447     // private
65448     restoreScroll : function(state){
65449         var sb = this.scroller.dom;
65450         sb.scrollLeft = state.left;
65451         sb.scrollTop = state.top;
65452     },
65453
65454     /**
65455      * Scrolls the grid to the top
65456      */
65457     scrollToTop : function(){
65458         this.scroller.dom.scrollTop = 0;
65459         this.scroller.dom.scrollLeft = 0;
65460     },
65461
65462     // private
65463     syncScroll : function(){
65464       this.syncHeaderScroll();
65465       var mb = this.scroller.dom;
65466         this.grid.fireEvent('bodyscroll', mb.scrollLeft, mb.scrollTop);
65467     },
65468
65469     // private
65470     syncHeaderScroll : function(){
65471         var mb = this.scroller.dom;
65472         this.innerHd.scrollLeft = mb.scrollLeft;
65473         this.innerHd.scrollLeft = mb.scrollLeft; // second time for IE (1/2 time first fails, other browsers ignore)
65474     },
65475
65476     // private
65477     updateSortIcon : function(col, dir){
65478         var sc = this.sortClasses;
65479         var hds = this.mainHd.select('td').removeClass(sc);
65480         hds.item(col).addClass(sc[dir == 'DESC' ? 1 : 0]);
65481     },
65482
65483     // private
65484     updateAllColumnWidths : function(){
65485         var tw = this.getTotalWidth(),
65486             clen = this.cm.getColumnCount(),
65487             ws = [],
65488             len,
65489             i;
65490         for(i = 0; i < clen; i++){
65491             ws[i] = this.getColumnWidth(i);
65492         }
65493         this.innerHd.firstChild.style.width = this.getOffsetWidth();
65494         this.innerHd.firstChild.firstChild.style.width = tw;
65495         this.mainBody.dom.style.width = tw;
65496         for(i = 0; i < clen; i++){
65497             var hd = this.getHeaderCell(i);
65498             hd.style.width = ws[i];
65499         }
65500
65501         var ns = this.getRows(), row, trow;
65502         for(i = 0, len = ns.length; i < len; i++){
65503             row = ns[i];
65504             row.style.width = tw;
65505             if(row.firstChild){
65506                 row.firstChild.style.width = tw;
65507                 trow = row.firstChild.rows[0];
65508                 for (var j = 0; j < clen; j++) {
65509                    trow.childNodes[j].style.width = ws[j];
65510                 }
65511             }
65512         }
65513
65514         this.onAllColumnWidthsUpdated(ws, tw);
65515     },
65516
65517     // private
65518     updateColumnWidth : function(col, width){
65519         var w = this.getColumnWidth(col);
65520         var tw = this.getTotalWidth();
65521         this.innerHd.firstChild.style.width = this.getOffsetWidth();
65522         this.innerHd.firstChild.firstChild.style.width = tw;
65523         this.mainBody.dom.style.width = tw;
65524         var hd = this.getHeaderCell(col);
65525         hd.style.width = w;
65526
65527         var ns = this.getRows(), row;
65528         for(var i = 0, len = ns.length; i < len; i++){
65529             row = ns[i];
65530             row.style.width = tw;
65531             if(row.firstChild){
65532                 row.firstChild.style.width = tw;
65533                 row.firstChild.rows[0].childNodes[col].style.width = w;
65534             }
65535         }
65536
65537         this.onColumnWidthUpdated(col, w, tw);
65538     },
65539
65540     // private
65541     updateColumnHidden : function(col, hidden){
65542         var tw = this.getTotalWidth();
65543         this.innerHd.firstChild.style.width = this.getOffsetWidth();
65544         this.innerHd.firstChild.firstChild.style.width = tw;
65545         this.mainBody.dom.style.width = tw;
65546         var display = hidden ? 'none' : '';
65547
65548         var hd = this.getHeaderCell(col);
65549         hd.style.display = display;
65550
65551         var ns = this.getRows(), row;
65552         for(var i = 0, len = ns.length; i < len; i++){
65553             row = ns[i];
65554             row.style.width = tw;
65555             if(row.firstChild){
65556                 row.firstChild.style.width = tw;
65557                 row.firstChild.rows[0].childNodes[col].style.display = display;
65558             }
65559         }
65560
65561         this.onColumnHiddenUpdated(col, hidden, tw);
65562         delete this.lastViewWidth; // force recalc
65563         this.layout();
65564     },
65565
65566     // private
65567     doRender : function(cs, rs, ds, startRow, colCount, stripe){
65568         var ts = this.templates, ct = ts.cell, rt = ts.row, last = colCount-1;
65569         var tstyle = 'width:'+this.getTotalWidth()+';';
65570         // buffers
65571         var buf = [], cb, c, p = {}, rp = {tstyle: tstyle}, r;
65572         for(var j = 0, len = rs.length; j < len; j++){
65573             r = rs[j]; cb = [];
65574             var rowIndex = (j+startRow);
65575             for(var i = 0; i < colCount; i++){
65576                 c = cs[i];
65577                 p.id = c.id;
65578                 p.css = i === 0 ? 'x-grid3-cell-first ' : (i == last ? 'x-grid3-cell-last ' : '');
65579                 p.attr = p.cellAttr = '';
65580                 p.value = c.renderer.call(c.scope, r.data[c.name], p, r, rowIndex, i, ds);
65581                 p.style = c.style;
65582                 if(Ext.isEmpty(p.value)){
65583                     p.value = '&#160;';
65584                 }
65585                 if(this.markDirty && r.dirty && Ext.isDefined(r.modified[c.name])){
65586                     p.css += ' x-grid3-dirty-cell';
65587                 }
65588                 cb[cb.length] = ct.apply(p);
65589             }
65590             var alt = [];
65591             if(stripe && ((rowIndex+1) % 2 === 0)){
65592                 alt[0] = 'x-grid3-row-alt';
65593             }
65594             if(r.dirty){
65595                 alt[1] = ' x-grid3-dirty-row';
65596             }
65597             rp.cols = colCount;
65598             if(this.getRowClass){
65599                 alt[2] = this.getRowClass(r, rowIndex, rp, ds);
65600             }
65601             rp.alt = alt.join(' ');
65602             rp.cells = cb.join('');
65603             buf[buf.length] =  rt.apply(rp);
65604         }
65605         return buf.join('');
65606     },
65607
65608     // private
65609     processRows : function(startRow, skipStripe){
65610         if(!this.ds || this.ds.getCount() < 1){
65611             return;
65612         }
65613         var rows = this.getRows(),
65614             len = rows.length,
65615             i, r;
65616             
65617         skipStripe = skipStripe || !this.grid.stripeRows;
65618         startRow = startRow || 0;
65619         for(i = 0; i<len; i++) {
65620             r = rows[i];
65621             if(r) {
65622                 r.rowIndex = i;
65623                 if(!skipStripe){
65624                     r.className = r.className.replace(this.rowClsRe, ' ');
65625                     if ((i + 1) % 2 === 0){
65626                         r.className += ' x-grid3-row-alt';
65627                     }
65628                 }   
65629             }          
65630         }
65631         // add first/last-row classes
65632         if(startRow === 0){
65633             Ext.fly(rows[0]).addClass(this.firstRowCls);
65634         }
65635         Ext.fly(rows[rows.length - 1]).addClass(this.lastRowCls);
65636     },
65637
65638     afterRender : function(){
65639         if(!this.ds || !this.cm){
65640             return;
65641         }
65642         this.mainBody.dom.innerHTML = this.renderRows() || '&#160;';
65643         this.processRows(0, true);
65644
65645         if(this.deferEmptyText !== true){
65646             this.applyEmptyText();
65647         }
65648         this.grid.fireEvent('viewready', this.grid);
65649     },
65650
65651     // private
65652     renderUI : function(){
65653
65654         var header = this.renderHeaders();
65655         var body = this.templates.body.apply({rows:'&#160;'});
65656
65657
65658         var html = this.templates.master.apply({
65659             body: body,
65660             header: header,
65661             ostyle: 'width:'+this.getOffsetWidth()+';',
65662             bstyle: 'width:'+this.getTotalWidth()+';'
65663         });
65664
65665         var g = this.grid;
65666
65667         g.getGridEl().dom.innerHTML = html;
65668
65669         this.initElements();
65670
65671         // get mousedowns early
65672         Ext.fly(this.innerHd).on('click', this.handleHdDown, this);
65673         this.mainHd.on({
65674             scope: this,
65675             mouseover: this.handleHdOver,
65676             mouseout: this.handleHdOut,
65677             mousemove: this.handleHdMove
65678         });
65679
65680         this.scroller.on('scroll', this.syncScroll,  this);
65681         if(g.enableColumnResize !== false){
65682             this.splitZone = new Ext.grid.GridView.SplitDragZone(g, this.mainHd.dom);
65683         }
65684
65685         if(g.enableColumnMove){
65686             this.columnDrag = new Ext.grid.GridView.ColumnDragZone(g, this.innerHd);
65687             this.columnDrop = new Ext.grid.HeaderDropZone(g, this.mainHd.dom);
65688         }
65689
65690         if(g.enableHdMenu !== false){
65691             this.hmenu = new Ext.menu.Menu({id: g.id + '-hctx'});
65692             this.hmenu.add(
65693                 {itemId:'asc', text: this.sortAscText, cls: 'xg-hmenu-sort-asc'},
65694                 {itemId:'desc', text: this.sortDescText, cls: 'xg-hmenu-sort-desc'}
65695             );
65696             if(g.enableColumnHide !== false){
65697                 this.colMenu = new Ext.menu.Menu({id:g.id + '-hcols-menu'});
65698                 this.colMenu.on({
65699                     scope: this,
65700                     beforeshow: this.beforeColMenuShow,
65701                     itemclick: this.handleHdMenuClick
65702                 });
65703                 this.hmenu.add('-', {
65704                     itemId:'columns',
65705                     hideOnClick: false,
65706                     text: this.columnsText,
65707                     menu: this.colMenu,
65708                     iconCls: 'x-cols-icon'
65709                 });
65710             }
65711             this.hmenu.on('itemclick', this.handleHdMenuClick, this);
65712         }
65713
65714         if(g.trackMouseOver){
65715             this.mainBody.on({
65716                 scope: this,
65717                 mouseover: this.onRowOver,
65718                 mouseout: this.onRowOut
65719             });
65720         }
65721
65722         if(g.enableDragDrop || g.enableDrag){
65723             this.dragZone = new Ext.grid.GridDragZone(g, {
65724                 ddGroup : g.ddGroup || 'GridDD'
65725             });
65726         }
65727
65728         this.updateHeaderSortState();
65729
65730     },
65731     
65732     // private
65733     processEvent: Ext.emptyFn,
65734
65735     // private
65736     layout : function(){
65737         if(!this.mainBody){
65738             return; // not rendered
65739         }
65740         var g = this.grid;
65741         var c = g.getGridEl();
65742         var csize = c.getSize(true);
65743         var vw = csize.width;
65744
65745         if(!g.hideHeaders && (vw < 20 || csize.height < 20)){ // display: none?
65746             return;
65747         }
65748         
65749         if(g.autoHeight){
65750             this.scroller.dom.style.overflow = 'visible';
65751             if(Ext.isWebKit){
65752                 this.scroller.dom.style.position = 'static';
65753             }
65754         }else{
65755             this.el.setSize(csize.width, csize.height);
65756
65757             var hdHeight = this.mainHd.getHeight();
65758             var vh = csize.height - (hdHeight);
65759
65760             this.scroller.setSize(vw, vh);
65761             if(this.innerHd){
65762                 this.innerHd.style.width = (vw)+'px';
65763             }
65764         }
65765         if(this.forceFit){
65766             if(this.lastViewWidth != vw){
65767                 this.fitColumns(false, false);
65768                 this.lastViewWidth = vw;
65769             }
65770         }else {
65771             this.autoExpand();
65772             this.syncHeaderScroll();
65773         }
65774         this.onLayout(vw, vh);
65775     },
65776
65777     // template functions for subclasses and plugins
65778     // these functions include precalculated values
65779     onLayout : function(vw, vh){
65780         // do nothing
65781     },
65782
65783     onColumnWidthUpdated : function(col, w, tw){
65784         //template method
65785     },
65786
65787     onAllColumnWidthsUpdated : function(ws, tw){
65788         //template method
65789     },
65790
65791     onColumnHiddenUpdated : function(col, hidden, tw){
65792         // template method
65793     },
65794
65795     updateColumnText : function(col, text){
65796         // template method
65797     },
65798
65799     afterMove : function(colIndex){
65800         // template method
65801     },
65802
65803     /* ----------------------------------- Core Specific -------------------------------------------*/
65804     // private
65805     init : function(grid){
65806         this.grid = grid;
65807
65808         this.initTemplates();
65809         this.initData(grid.store, grid.colModel);
65810         this.initUI(grid);
65811     },
65812
65813     // private
65814     getColumnId : function(index){
65815       return this.cm.getColumnId(index);
65816     },
65817     
65818     // private 
65819     getOffsetWidth : function() {
65820         return (this.cm.getTotalWidth() + this.getScrollOffset()) + 'px';
65821     },
65822     
65823     getScrollOffset: function(){
65824         return Ext.num(this.scrollOffset, Ext.getScrollBarWidth());
65825     },
65826
65827     // private
65828     renderHeaders : function(){
65829         var cm = this.cm, 
65830             ts = this.templates,
65831             ct = ts.hcell,
65832             cb = [], 
65833             p = {},
65834             len = cm.getColumnCount(),
65835             last = len - 1;
65836             
65837         for(var i = 0; i < len; i++){
65838             p.id = cm.getColumnId(i);
65839             p.value = cm.getColumnHeader(i) || '';
65840             p.style = this.getColumnStyle(i, true);
65841             p.tooltip = this.getColumnTooltip(i);
65842             p.css = i === 0 ? 'x-grid3-cell-first ' : (i == last ? 'x-grid3-cell-last ' : '');
65843             if(cm.config[i].align == 'right'){
65844                 p.istyle = 'padding-right:16px';
65845             } else {
65846                 delete p.istyle;
65847             }
65848             cb[cb.length] = ct.apply(p);
65849         }
65850         return ts.header.apply({cells: cb.join(''), tstyle:'width:'+this.getTotalWidth()+';'});
65851     },
65852
65853     // private
65854     getColumnTooltip : function(i){
65855         var tt = this.cm.getColumnTooltip(i);
65856         if(tt){
65857             if(Ext.QuickTips.isEnabled()){
65858                 return 'ext:qtip="'+tt+'"';
65859             }else{
65860                 return 'title="'+tt+'"';
65861             }
65862         }
65863         return '';
65864     },
65865
65866     // private
65867     beforeUpdate : function(){
65868         this.grid.stopEditing(true);
65869     },
65870
65871     // private
65872     updateHeaders : function(){
65873         this.innerHd.firstChild.innerHTML = this.renderHeaders();
65874         this.innerHd.firstChild.style.width = this.getOffsetWidth();
65875         this.innerHd.firstChild.firstChild.style.width = this.getTotalWidth();
65876     },
65877
65878     /**
65879      * Focuses the specified row.
65880      * @param {Number} row The row index
65881      */
65882     focusRow : function(row){
65883         this.focusCell(row, 0, false);
65884     },
65885
65886     /**
65887      * Focuses the specified cell.
65888      * @param {Number} row The row index
65889      * @param {Number} col The column index
65890      */
65891     focusCell : function(row, col, hscroll){
65892         this.syncFocusEl(this.ensureVisible(row, col, hscroll));
65893         if(Ext.isGecko){
65894             this.focusEl.focus();
65895         }else{
65896             this.focusEl.focus.defer(1, this.focusEl);
65897         }
65898     },
65899
65900     resolveCell : function(row, col, hscroll){
65901         if(!Ext.isNumber(row)){
65902             row = row.rowIndex;
65903         }
65904         if(!this.ds){
65905             return null;
65906         }
65907         if(row < 0 || row >= this.ds.getCount()){
65908             return null;
65909         }
65910         col = (col !== undefined ? col : 0);
65911
65912         var rowEl = this.getRow(row),
65913             cm = this.cm,
65914             colCount = cm.getColumnCount(),
65915             cellEl;
65916         if(!(hscroll === false && col === 0)){
65917             while(col < colCount && cm.isHidden(col)){
65918                 col++;
65919             }
65920             cellEl = this.getCell(row, col);
65921         }
65922
65923         return {row: rowEl, cell: cellEl};
65924     },
65925
65926     getResolvedXY : function(resolved){
65927         if(!resolved){
65928             return null;
65929         }
65930         var s = this.scroller.dom, c = resolved.cell, r = resolved.row;
65931         return c ? Ext.fly(c).getXY() : [this.el.getX(), Ext.fly(r).getY()];
65932     },
65933
65934     syncFocusEl : function(row, col, hscroll){
65935         var xy = row;
65936         if(!Ext.isArray(xy)){
65937             row = Math.min(row, Math.max(0, this.getRows().length-1));
65938             xy = this.getResolvedXY(this.resolveCell(row, col, hscroll));
65939         }
65940         this.focusEl.setXY(xy||this.scroller.getXY());
65941     },
65942
65943     ensureVisible : function(row, col, hscroll){
65944         var resolved = this.resolveCell(row, col, hscroll);
65945         if(!resolved || !resolved.row){
65946             return;
65947         }
65948
65949         var rowEl = resolved.row, 
65950             cellEl = resolved.cell,
65951             c = this.scroller.dom,
65952             ctop = 0,
65953             p = rowEl, 
65954             stop = this.el.dom;
65955             
65956         while(p && p != stop){
65957             ctop += p.offsetTop;
65958             p = p.offsetParent;
65959         }
65960         
65961         ctop -= this.mainHd.dom.offsetHeight;
65962         stop = parseInt(c.scrollTop, 10);
65963         
65964         var cbot = ctop + rowEl.offsetHeight,
65965             ch = c.clientHeight,
65966             sbot = stop + ch;
65967         
65968
65969         if(ctop < stop){
65970           c.scrollTop = ctop;
65971         }else if(cbot > sbot){
65972             c.scrollTop = cbot-ch;
65973         }
65974
65975         if(hscroll !== false){
65976             var cleft = parseInt(cellEl.offsetLeft, 10);
65977             var cright = cleft + cellEl.offsetWidth;
65978
65979             var sleft = parseInt(c.scrollLeft, 10);
65980             var sright = sleft + c.clientWidth;
65981             if(cleft < sleft){
65982                 c.scrollLeft = cleft;
65983             }else if(cright > sright){
65984                 c.scrollLeft = cright-c.clientWidth;
65985             }
65986         }
65987         return this.getResolvedXY(resolved);
65988     },
65989
65990     // private
65991     insertRows : function(dm, firstRow, lastRow, isUpdate){
65992         var last = dm.getCount() - 1;
65993         if(!isUpdate && firstRow === 0 && lastRow >= last){
65994             this.fireEvent('beforerowsinserted', this, firstRow, lastRow);
65995             this.refresh();
65996             this.fireEvent('rowsinserted', this, firstRow, lastRow);
65997         }else{
65998             if(!isUpdate){
65999                 this.fireEvent('beforerowsinserted', this, firstRow, lastRow);
66000             }
66001             var html = this.renderRows(firstRow, lastRow),
66002                 before = this.getRow(firstRow);
66003             if(before){
66004                 if(firstRow === 0){
66005                     Ext.fly(this.getRow(0)).removeClass(this.firstRowCls);
66006                 }
66007                 Ext.DomHelper.insertHtml('beforeBegin', before, html);
66008             }else{
66009                 var r = this.getRow(last - 1);
66010                 if(r){
66011                     Ext.fly(r).removeClass(this.lastRowCls);
66012                 }
66013                 Ext.DomHelper.insertHtml('beforeEnd', this.mainBody.dom, html);
66014             }
66015             if(!isUpdate){
66016                 this.fireEvent('rowsinserted', this, firstRow, lastRow);
66017                 this.processRows(firstRow);
66018             }else if(firstRow === 0 || firstRow >= last){
66019                 //ensure first/last row is kept after an update.
66020                 Ext.fly(this.getRow(firstRow)).addClass(firstRow === 0 ? this.firstRowCls : this.lastRowCls);
66021             }
66022         }
66023         this.syncFocusEl(firstRow);
66024     },
66025
66026     // private
66027     deleteRows : function(dm, firstRow, lastRow){
66028         if(dm.getRowCount()<1){
66029             this.refresh();
66030         }else{
66031             this.fireEvent('beforerowsdeleted', this, firstRow, lastRow);
66032
66033             this.removeRows(firstRow, lastRow);
66034
66035             this.processRows(firstRow);
66036             this.fireEvent('rowsdeleted', this, firstRow, lastRow);
66037         }
66038     },
66039
66040     // private
66041     getColumnStyle : function(col, isHeader){
66042         var style = !isHeader ? (this.cm.config[col].css || '') : '';
66043         style += 'width:'+this.getColumnWidth(col)+';';
66044         if(this.cm.isHidden(col)){
66045             style += 'display:none;';
66046         }
66047         var align = this.cm.config[col].align;
66048         if(align){
66049             style += 'text-align:'+align+';';
66050         }
66051         return style;
66052     },
66053
66054     // private
66055     getColumnWidth : function(col){
66056         var w = this.cm.getColumnWidth(col);
66057         if(Ext.isNumber(w)){
66058             return (Ext.isBorderBox || (Ext.isWebKit && !Ext.isSafari2) ? w : (w - this.borderWidth > 0 ? w - this.borderWidth : 0)) + 'px';
66059         }
66060         return w;
66061     },
66062
66063     // private
66064     getTotalWidth : function(){
66065         return this.cm.getTotalWidth()+'px';
66066     },
66067
66068     // private
66069     fitColumns : function(preventRefresh, onlyExpand, omitColumn){
66070         var cm = this.cm, i;
66071         var tw = cm.getTotalWidth(false);
66072         var aw = this.grid.getGridEl().getWidth(true)-this.getScrollOffset();
66073
66074         if(aw < 20){ // not initialized, so don't screw up the default widths
66075             return;
66076         }
66077         var extra = aw - tw;
66078
66079         if(extra === 0){
66080             return false;
66081         }
66082
66083         var vc = cm.getColumnCount(true);
66084         var ac = vc-(Ext.isNumber(omitColumn) ? 1 : 0);
66085         if(ac === 0){
66086             ac = 1;
66087             omitColumn = undefined;
66088         }
66089         var colCount = cm.getColumnCount();
66090         var cols = [];
66091         var extraCol = 0;
66092         var width = 0;
66093         var w;
66094         for (i = 0; i < colCount; i++){
66095             if(!cm.isHidden(i) && !cm.isFixed(i) && i !== omitColumn){
66096                 w = cm.getColumnWidth(i);
66097                 cols.push(i);
66098                 extraCol = i;
66099                 cols.push(w);
66100                 width += w;
66101             }
66102         }
66103         var frac = (aw - cm.getTotalWidth())/width;
66104         while (cols.length){
66105             w = cols.pop();
66106             i = cols.pop();
66107             cm.setColumnWidth(i, Math.max(this.grid.minColumnWidth, Math.floor(w + w*frac)), true);
66108         }
66109
66110         if((tw = cm.getTotalWidth(false)) > aw){
66111             var adjustCol = ac != vc ? omitColumn : extraCol;
66112              cm.setColumnWidth(adjustCol, Math.max(1,
66113                      cm.getColumnWidth(adjustCol)- (tw-aw)), true);
66114         }
66115
66116         if(preventRefresh !== true){
66117             this.updateAllColumnWidths();
66118         }
66119
66120
66121         return true;
66122     },
66123
66124     // private
66125     autoExpand : function(preventUpdate){
66126         var g = this.grid, cm = this.cm;
66127         if(!this.userResized && g.autoExpandColumn){
66128             var tw = cm.getTotalWidth(false);
66129             var aw = this.grid.getGridEl().getWidth(true)-this.getScrollOffset();
66130             if(tw != aw){
66131                 var ci = cm.getIndexById(g.autoExpandColumn);
66132                 var currentWidth = cm.getColumnWidth(ci);
66133                 var cw = Math.min(Math.max(((aw-tw)+currentWidth), g.autoExpandMin), g.autoExpandMax);
66134                 if(cw != currentWidth){
66135                     cm.setColumnWidth(ci, cw, true);
66136                     if(preventUpdate !== true){
66137                         this.updateColumnWidth(ci, cw);
66138                     }
66139                 }
66140             }
66141         }
66142     },
66143
66144     // private
66145     getColumnData : function(){
66146         // build a map for all the columns
66147         var cs = [], cm = this.cm, colCount = cm.getColumnCount();
66148         for(var i = 0; i < colCount; i++){
66149             var name = cm.getDataIndex(i);
66150             cs[i] = {
66151                 name : (!Ext.isDefined(name) ? this.ds.fields.get(i).name : name),
66152                 renderer : cm.getRenderer(i),
66153                 scope: cm.getRendererScope(i),
66154                 id : cm.getColumnId(i),
66155                 style : this.getColumnStyle(i)
66156             };
66157         }
66158         return cs;
66159     },
66160
66161     // private
66162     renderRows : function(startRow, endRow){
66163         // pull in all the crap needed to render rows
66164         var g = this.grid, cm = g.colModel, ds = g.store, stripe = g.stripeRows;
66165         var colCount = cm.getColumnCount();
66166
66167         if(ds.getCount() < 1){
66168             return '';
66169         }
66170
66171         var cs = this.getColumnData();
66172
66173         startRow = startRow || 0;
66174         endRow = !Ext.isDefined(endRow) ? ds.getCount()-1 : endRow;
66175
66176         // records to render
66177         var rs = ds.getRange(startRow, endRow);
66178
66179         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
66180     },
66181
66182     // private
66183     renderBody : function(){
66184         var markup = this.renderRows() || '&#160;';
66185         return this.templates.body.apply({rows: markup});
66186     },
66187
66188     // private
66189     refreshRow : function(record){
66190         var ds = this.ds, index;
66191         if(Ext.isNumber(record)){
66192             index = record;
66193             record = ds.getAt(index);
66194             if(!record){
66195                 return;
66196             }
66197         }else{
66198             index = ds.indexOf(record);
66199             if(index < 0){
66200                 return;
66201             }
66202         }
66203         this.insertRows(ds, index, index, true);
66204         this.getRow(index).rowIndex = index;
66205         this.onRemove(ds, record, index+1, true);
66206         this.fireEvent('rowupdated', this, index, record);
66207     },
66208
66209     /**
66210      * Refreshs the grid UI
66211      * @param {Boolean} headersToo (optional) True to also refresh the headers
66212      */
66213     refresh : function(headersToo){
66214         this.fireEvent('beforerefresh', this);
66215         this.grid.stopEditing(true);
66216
66217         var result = this.renderBody();
66218         this.mainBody.update(result).setWidth(this.getTotalWidth());
66219         if(headersToo === true){
66220             this.updateHeaders();
66221             this.updateHeaderSortState();
66222         }
66223         this.processRows(0, true);
66224         this.layout();
66225         this.applyEmptyText();
66226         this.fireEvent('refresh', this);
66227     },
66228
66229     // private
66230     applyEmptyText : function(){
66231         if(this.emptyText && !this.hasRows()){
66232             this.mainBody.update('<div class="x-grid-empty">' + this.emptyText + '</div>');
66233         }
66234     },
66235
66236     // private
66237     updateHeaderSortState : function(){
66238         var state = this.ds.getSortState();
66239         if(!state){
66240             return;
66241         }
66242         if(!this.sortState || (this.sortState.field != state.field || this.sortState.direction != state.direction)){
66243             this.grid.fireEvent('sortchange', this.grid, state);
66244         }
66245         this.sortState = state;
66246         var sortColumn = this.cm.findColumnIndex(state.field);
66247         if(sortColumn != -1){
66248             var sortDir = state.direction;
66249             this.updateSortIcon(sortColumn, sortDir);
66250         }
66251     },
66252
66253     // private
66254     clearHeaderSortState : function(){
66255         if(!this.sortState){
66256             return;
66257         }
66258         this.grid.fireEvent('sortchange', this.grid, null);
66259         this.mainHd.select('td').removeClass(this.sortClasses);
66260         delete this.sortState;
66261     },
66262
66263     // private
66264     destroy : function(){
66265         if(this.colMenu){
66266             Ext.menu.MenuMgr.unregister(this.colMenu);
66267             this.colMenu.destroy();
66268             delete this.colMenu;
66269         }
66270         if(this.hmenu){
66271             Ext.menu.MenuMgr.unregister(this.hmenu);
66272             this.hmenu.destroy();
66273             delete this.hmenu;
66274         }
66275
66276         this.initData(null, null);
66277         this.purgeListeners();
66278         Ext.fly(this.innerHd).un("click", this.handleHdDown, this);
66279
66280         if(this.grid.enableColumnMove){
66281             Ext.destroy(
66282                 this.columnDrag.el,
66283                 this.columnDrag.proxy.ghost,
66284                 this.columnDrag.proxy.el,
66285                 this.columnDrop.el,
66286                 this.columnDrop.proxyTop,
66287                 this.columnDrop.proxyBottom,
66288                 this.columnDrag.dragData.ddel,
66289                 this.columnDrag.dragData.header
66290             );
66291             if (this.columnDrag.proxy.anim) {
66292                 Ext.destroy(this.columnDrag.proxy.anim);
66293             }
66294             delete this.columnDrag.proxy.ghost;
66295             delete this.columnDrag.dragData.ddel;
66296             delete this.columnDrag.dragData.header;
66297             this.columnDrag.destroy();
66298             delete Ext.dd.DDM.locationCache[this.columnDrag.id];
66299             delete this.columnDrag._domRef;
66300
66301             delete this.columnDrop.proxyTop;
66302             delete this.columnDrop.proxyBottom;
66303             this.columnDrop.destroy();
66304             delete Ext.dd.DDM.locationCache["gridHeader" + this.grid.getGridEl().id];
66305             delete this.columnDrop._domRef;
66306             delete Ext.dd.DDM.ids[this.columnDrop.ddGroup];
66307         }
66308
66309         if (this.splitZone){ // enableColumnResize
66310             this.splitZone.destroy();
66311             delete this.splitZone._domRef;
66312             delete Ext.dd.DDM.ids["gridSplitters" + this.grid.getGridEl().id];
66313         }
66314
66315         Ext.fly(this.innerHd).removeAllListeners();
66316         Ext.removeNode(this.innerHd);
66317         delete this.innerHd;
66318
66319         Ext.destroy(
66320             this.el,
66321             this.mainWrap,
66322             this.mainHd,
66323             this.scroller,
66324             this.mainBody,
66325             this.focusEl,
66326             this.resizeMarker,
66327             this.resizeProxy,
66328             this.activeHdBtn,
66329             this.dragZone,
66330             this.splitZone,
66331             this._flyweight
66332         );
66333
66334         delete this.grid.container;
66335
66336         if(this.dragZone){
66337             this.dragZone.destroy();
66338         }
66339
66340         Ext.dd.DDM.currentTarget = null;
66341         delete Ext.dd.DDM.locationCache[this.grid.getGridEl().id];
66342
66343         Ext.EventManager.removeResizeListener(this.onWindowResize, this);
66344     },
66345
66346     // private
66347     onDenyColumnHide : function(){
66348
66349     },
66350
66351     // private
66352     render : function(){
66353         if(this.autoFill){
66354             var ct = this.grid.ownerCt;
66355             if (ct && ct.getLayout()){
66356                 ct.on('afterlayout', function(){ 
66357                     this.fitColumns(true, true);
66358                     this.updateHeaders(); 
66359                 }, this, {single: true}); 
66360             }else{ 
66361                 this.fitColumns(true, true); 
66362             }
66363         }else if(this.forceFit){
66364             this.fitColumns(true, false);
66365         }else if(this.grid.autoExpandColumn){
66366             this.autoExpand(true);
66367         }
66368
66369         this.renderUI();
66370     },
66371
66372     /* --------------------------------- Model Events and Handlers --------------------------------*/
66373     // private
66374     initData : function(ds, cm){
66375         if(this.ds){
66376             this.ds.un('load', this.onLoad, this);
66377             this.ds.un('datachanged', this.onDataChange, this);
66378             this.ds.un('add', this.onAdd, this);
66379             this.ds.un('remove', this.onRemove, this);
66380             this.ds.un('update', this.onUpdate, this);
66381             this.ds.un('clear', this.onClear, this);
66382             if(this.ds !== ds && this.ds.autoDestroy){
66383                 this.ds.destroy();
66384             }
66385         }
66386         if(ds){
66387             ds.on({
66388                 scope: this,
66389                 load: this.onLoad,
66390                 datachanged: this.onDataChange,
66391                 add: this.onAdd,
66392                 remove: this.onRemove,
66393                 update: this.onUpdate,
66394                 clear: this.onClear
66395             });
66396         }
66397         this.ds = ds;
66398
66399         if(this.cm){
66400             this.cm.un('configchange', this.onColConfigChange, this);
66401             this.cm.un('widthchange', this.onColWidthChange, this);
66402             this.cm.un('headerchange', this.onHeaderChange, this);
66403             this.cm.un('hiddenchange', this.onHiddenChange, this);
66404             this.cm.un('columnmoved', this.onColumnMove, this);
66405         }
66406         if(cm){
66407             delete this.lastViewWidth;
66408             cm.on({
66409                 scope: this,
66410                 configchange: this.onColConfigChange,
66411                 widthchange: this.onColWidthChange,
66412                 headerchange: this.onHeaderChange,
66413                 hiddenchange: this.onHiddenChange,
66414                 columnmoved: this.onColumnMove
66415             });
66416         }
66417         this.cm = cm;
66418     },
66419
66420     // private
66421     onDataChange : function(){
66422         this.refresh();
66423         this.updateHeaderSortState();
66424         this.syncFocusEl(0);
66425     },
66426
66427     // private
66428     onClear : function(){
66429         this.refresh();
66430         this.syncFocusEl(0);
66431     },
66432
66433     // private
66434     onUpdate : function(ds, record){
66435         this.refreshRow(record);
66436     },
66437
66438     // private
66439     onAdd : function(ds, records, index){
66440         
66441         this.insertRows(ds, index, index + (records.length-1));
66442     },
66443
66444     // private
66445     onRemove : function(ds, record, index, isUpdate){
66446         if(isUpdate !== true){
66447             this.fireEvent('beforerowremoved', this, index, record);
66448         }
66449         this.removeRow(index);
66450         if(isUpdate !== true){
66451             this.processRows(index);
66452             this.applyEmptyText();
66453             this.fireEvent('rowremoved', this, index, record);
66454         }
66455     },
66456
66457     // private
66458     onLoad : function(){
66459         this.scrollToTop.defer(Ext.isGecko ? 1 : 0, this);
66460     },
66461
66462     // private
66463     onColWidthChange : function(cm, col, width){
66464         this.updateColumnWidth(col, width);
66465     },
66466
66467     // private
66468     onHeaderChange : function(cm, col, text){
66469         this.updateHeaders();
66470     },
66471
66472     // private
66473     onHiddenChange : function(cm, col, hidden){
66474         this.updateColumnHidden(col, hidden);
66475     },
66476
66477     // private
66478     onColumnMove : function(cm, oldIndex, newIndex){
66479         this.indexMap = null;
66480         var s = this.getScrollState();
66481         this.refresh(true);
66482         this.restoreScroll(s);
66483         this.afterMove(newIndex);
66484         this.grid.fireEvent('columnmove', oldIndex, newIndex);
66485     },
66486
66487     // private
66488     onColConfigChange : function(){
66489         delete this.lastViewWidth;
66490         this.indexMap = null;
66491         this.refresh(true);
66492     },
66493
66494     /* -------------------- UI Events and Handlers ------------------------------ */
66495     // private
66496     initUI : function(grid){
66497         grid.on('headerclick', this.onHeaderClick, this);
66498     },
66499
66500     // private
66501     initEvents : function(){
66502     },
66503
66504     // private
66505     onHeaderClick : function(g, index){
66506         if(this.headersDisabled || !this.cm.isSortable(index)){
66507             return;
66508         }
66509         g.stopEditing(true);
66510         g.store.sort(this.cm.getDataIndex(index));
66511     },
66512
66513     // private
66514     onRowOver : function(e, t){
66515         var row;
66516         if((row = this.findRowIndex(t)) !== false){
66517             this.addRowClass(row, 'x-grid3-row-over');
66518         }
66519     },
66520
66521     // private
66522     onRowOut : function(e, t){
66523         var row;
66524         if((row = this.findRowIndex(t)) !== false && !e.within(this.getRow(row), true)){
66525             this.removeRowClass(row, 'x-grid3-row-over');
66526         }
66527     },
66528
66529     // private
66530     handleWheel : function(e){
66531         e.stopPropagation();
66532     },
66533
66534     // private
66535     onRowSelect : function(row){
66536         this.addRowClass(row, this.selectedRowClass);
66537     },
66538
66539     // private
66540     onRowDeselect : function(row){
66541         this.removeRowClass(row, this.selectedRowClass);
66542     },
66543
66544     // private
66545     onCellSelect : function(row, col){
66546         var cell = this.getCell(row, col);
66547         if(cell){
66548             this.fly(cell).addClass('x-grid3-cell-selected');
66549         }
66550     },
66551
66552     // private
66553     onCellDeselect : function(row, col){
66554         var cell = this.getCell(row, col);
66555         if(cell){
66556             this.fly(cell).removeClass('x-grid3-cell-selected');
66557         }
66558     },
66559
66560     // private
66561     onColumnSplitterMoved : function(i, w){
66562         this.userResized = true;
66563         var cm = this.grid.colModel;
66564         cm.setColumnWidth(i, w, true);
66565
66566         if(this.forceFit){
66567             this.fitColumns(true, false, i);
66568             this.updateAllColumnWidths();
66569         }else{
66570             this.updateColumnWidth(i, w);
66571             this.syncHeaderScroll();
66572         }
66573
66574         this.grid.fireEvent('columnresize', i, w);
66575     },
66576
66577     // private
66578     handleHdMenuClick : function(item){
66579         var index = this.hdCtxIndex,
66580             cm = this.cm, 
66581             ds = this.ds,
66582             id = item.getItemId();
66583         switch(id){
66584             case 'asc':
66585                 ds.sort(cm.getDataIndex(index), 'ASC');
66586                 break;
66587             case 'desc':
66588                 ds.sort(cm.getDataIndex(index), 'DESC');
66589                 break;
66590             default:
66591                 index = cm.getIndexById(id.substr(4));
66592                 if(index != -1){
66593                     if(item.checked && cm.getColumnsBy(this.isHideableColumn, this).length <= 1){
66594                         this.onDenyColumnHide();
66595                         return false;
66596                     }
66597                     cm.setHidden(index, item.checked);
66598                 }
66599         }
66600         return true;
66601     },
66602
66603     // private
66604     isHideableColumn : function(c){
66605         return !c.hidden && !c.fixed;
66606     },
66607
66608     // private
66609     beforeColMenuShow : function(){
66610         var cm = this.cm,  colCount = cm.getColumnCount();
66611         this.colMenu.removeAll();
66612         for(var i = 0; i < colCount; i++){
66613             if(cm.config[i].fixed !== true && cm.config[i].hideable !== false){
66614                 this.colMenu.add(new Ext.menu.CheckItem({
66615                     itemId: 'col-'+cm.getColumnId(i),
66616                     text: cm.getColumnHeader(i),
66617                     checked: !cm.isHidden(i),
66618                     hideOnClick:false,
66619                     disabled: cm.config[i].hideable === false
66620                 }));
66621             }
66622         }
66623     },
66624
66625     // private
66626     handleHdDown : function(e, t){
66627         if(Ext.fly(t).hasClass('x-grid3-hd-btn')){
66628             e.stopEvent();
66629             var hd = this.findHeaderCell(t);
66630             Ext.fly(hd).addClass('x-grid3-hd-menu-open');
66631             var index = this.getCellIndex(hd);
66632             this.hdCtxIndex = index;
66633             var ms = this.hmenu.items, cm = this.cm;
66634             ms.get('asc').setDisabled(!cm.isSortable(index));
66635             ms.get('desc').setDisabled(!cm.isSortable(index));
66636             this.hmenu.on('hide', function(){
66637                 Ext.fly(hd).removeClass('x-grid3-hd-menu-open');
66638             }, this, {single:true});
66639             this.hmenu.show(t, 'tl-bl?');
66640         }
66641     },
66642
66643     // private
66644     handleHdOver : function(e, t){
66645         var hd = this.findHeaderCell(t);
66646         if(hd && !this.headersDisabled){
66647             this.activeHdRef = t;
66648             this.activeHdIndex = this.getCellIndex(hd);
66649             var fly = this.fly(hd);
66650             this.activeHdRegion = fly.getRegion();
66651             if(!this.cm.isMenuDisabled(this.activeHdIndex)){
66652                 fly.addClass('x-grid3-hd-over');
66653                 this.activeHdBtn = fly.child('.x-grid3-hd-btn');
66654                 if(this.activeHdBtn){
66655                     this.activeHdBtn.dom.style.height = (hd.firstChild.offsetHeight-1)+'px';
66656                 }
66657             }
66658         }
66659     },
66660
66661     // private
66662     handleHdMove : function(e, t){
66663         var hd = this.findHeaderCell(this.activeHdRef);
66664         if(hd && !this.headersDisabled){
66665             var hw = this.splitHandleWidth || 5,
66666                 r = this.activeHdRegion,
66667                 x = e.getPageX(),
66668                 ss = hd.style,
66669                 cur = '';
66670             if(this.grid.enableColumnResize !== false){
66671                 if(x - r.left <= hw && this.cm.isResizable(this.activeHdIndex-1)){
66672                     cur = Ext.isAir ? 'move' : Ext.isWebKit ? 'e-resize' : 'col-resize'; // col-resize not always supported
66673                 }else if(r.right - x <= (!this.activeHdBtn ? hw : 2) && this.cm.isResizable(this.activeHdIndex)){
66674                     cur = Ext.isAir ? 'move' : Ext.isWebKit ? 'w-resize' : 'col-resize';
66675                 }
66676             }
66677             ss.cursor = cur;
66678         }
66679     },
66680
66681     // private
66682     handleHdOut : function(e, t){
66683         var hd = this.findHeaderCell(t);
66684         if(hd && (!Ext.isIE || !e.within(hd, true))){
66685             this.activeHdRef = null;
66686             this.fly(hd).removeClass('x-grid3-hd-over');
66687             hd.style.cursor = '';
66688         }
66689     },
66690
66691     // private
66692     hasRows : function(){
66693         var fc = this.mainBody.dom.firstChild;
66694         return fc && fc.nodeType == 1 && fc.className != 'x-grid-empty';
66695     },
66696
66697     // back compat
66698     bind : function(d, c){
66699         this.initData(d, c);
66700     }
66701 });
66702
66703
66704 // private
66705 // This is a support class used internally by the Grid components
66706 Ext.grid.GridView.SplitDragZone = function(grid, hd){
66707     this.grid = grid;
66708     this.view = grid.getView();
66709     this.marker = this.view.resizeMarker;
66710     this.proxy = this.view.resizeProxy;
66711     Ext.grid.GridView.SplitDragZone.superclass.constructor.call(this, hd,
66712         'gridSplitters' + this.grid.getGridEl().id, {
66713         dragElId : Ext.id(this.proxy.dom), resizeFrame:false
66714     });
66715     this.scroll = false;
66716     this.hw = this.view.splitHandleWidth || 5;
66717 };
66718 Ext.extend(Ext.grid.GridView.SplitDragZone, Ext.dd.DDProxy, {
66719
66720     b4StartDrag : function(x, y){
66721         this.view.headersDisabled = true;
66722         var h = this.view.mainWrap.getHeight();
66723         this.marker.setHeight(h);
66724         this.marker.show();
66725         this.marker.alignTo(this.view.getHeaderCell(this.cellIndex), 'tl-tl', [-2, 0]);
66726         this.proxy.setHeight(h);
66727         var w = this.cm.getColumnWidth(this.cellIndex);
66728         var minw = Math.max(w-this.grid.minColumnWidth, 0);
66729         this.resetConstraints();
66730         this.setXConstraint(minw, 1000);
66731         this.setYConstraint(0, 0);
66732         this.minX = x - minw;
66733         this.maxX = x + 1000;
66734         this.startPos = x;
66735         Ext.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
66736     },
66737     
66738     allowHeaderDrag : function(e){
66739         return true;
66740     },
66741
66742
66743     handleMouseDown : function(e){
66744         var t = this.view.findHeaderCell(e.getTarget());
66745         if(t && this.allowHeaderDrag(e)){
66746             var xy = this.view.fly(t).getXY(), x = xy[0], y = xy[1];
66747             var exy = e.getXY(), ex = exy[0];
66748             var w = t.offsetWidth, adjust = false;
66749             if((ex - x) <= this.hw){
66750                 adjust = -1;
66751             }else if((x+w) - ex <= this.hw){
66752                 adjust = 0;
66753             }
66754             if(adjust !== false){
66755                 this.cm = this.grid.colModel;
66756                 var ci = this.view.getCellIndex(t);
66757                 if(adjust == -1){
66758                   if (ci + adjust < 0) {
66759                     return;
66760                   }
66761                     while(this.cm.isHidden(ci+adjust)){
66762                         --adjust;
66763                         if(ci+adjust < 0){
66764                             return;
66765                         }
66766                     }
66767                 }
66768                 this.cellIndex = ci+adjust;
66769                 this.split = t.dom;
66770                 if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
66771                     Ext.grid.GridView.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
66772                 }
66773             }else if(this.view.columnDrag){
66774                 this.view.columnDrag.callHandleMouseDown(e);
66775             }
66776         }
66777     },
66778
66779     endDrag : function(e){
66780         this.marker.hide();
66781         var v = this.view;
66782         var endX = Math.max(this.minX, e.getPageX());
66783         var diff = endX - this.startPos;
66784         v.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
66785         setTimeout(function(){
66786             v.headersDisabled = false;
66787         }, 50);
66788     },
66789
66790     autoOffset : function(){
66791         this.setDelta(0,0);
66792     }
66793 });
66794 // private\r
66795 // This is a support class used internally by the Grid components\r
66796 Ext.grid.HeaderDragZone = Ext.extend(Ext.dd.DragZone, {\r
66797     maxDragWidth: 120,\r
66798     \r
66799     constructor : function(grid, hd, hd2){\r
66800         this.grid = grid;\r
66801         this.view = grid.getView();\r
66802         this.ddGroup = "gridHeader" + this.grid.getGridEl().id;\r
66803         Ext.grid.HeaderDragZone.superclass.constructor.call(this, hd);\r
66804         if(hd2){\r
66805             this.setHandleElId(Ext.id(hd));\r
66806             this.setOuterHandleElId(Ext.id(hd2));\r
66807         }\r
66808         this.scroll = false;\r
66809     },\r
66810     \r
66811     getDragData : function(e){\r
66812         var t = Ext.lib.Event.getTarget(e);\r
66813         var h = this.view.findHeaderCell(t);\r
66814         if(h){\r
66815             return {ddel: h.firstChild, header:h};\r
66816         }\r
66817         return false;\r
66818     },\r
66819 \r
66820     onInitDrag : function(e){\r
66821         this.view.headersDisabled = true;\r
66822         var clone = this.dragData.ddel.cloneNode(true);\r
66823         clone.id = Ext.id();\r
66824         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";\r
66825         this.proxy.update(clone);\r
66826         return true;\r
66827     },\r
66828 \r
66829     afterValidDrop : function(){\r
66830         var v = this.view;\r
66831         setTimeout(function(){\r
66832             v.headersDisabled = false;\r
66833         }, 50);\r
66834     },\r
66835 \r
66836     afterInvalidDrop : function(){\r
66837         var v = this.view;\r
66838         setTimeout(function(){\r
66839             v.headersDisabled = false;\r
66840         }, 50);\r
66841     }\r
66842 });\r
66843 \r
66844 // private\r
66845 // This is a support class used internally by the Grid components\r
66846 Ext.grid.HeaderDropZone = Ext.extend(Ext.dd.DropZone, {\r
66847     proxyOffsets : [-4, -9],\r
66848     fly: Ext.Element.fly,\r
66849     \r
66850     constructor : function(grid, hd, hd2){\r
66851         this.grid = grid;\r
66852         this.view = grid.getView();\r
66853         // split the proxies so they don't interfere with mouse events\r
66854         this.proxyTop = Ext.DomHelper.append(document.body, {\r
66855             cls:"col-move-top", html:"&#160;"\r
66856         }, true);\r
66857         this.proxyBottom = Ext.DomHelper.append(document.body, {\r
66858             cls:"col-move-bottom", html:"&#160;"\r
66859         }, true);\r
66860         this.proxyTop.hide = this.proxyBottom.hide = function(){\r
66861             this.setLeftTop(-100,-100);\r
66862             this.setStyle("visibility", "hidden");\r
66863         };\r
66864         this.ddGroup = "gridHeader" + this.grid.getGridEl().id;\r
66865         // temporarily disabled\r
66866         //Ext.dd.ScrollManager.register(this.view.scroller.dom);\r
66867         Ext.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);\r
66868     },\r
66869 \r
66870     getTargetFromEvent : function(e){\r
66871         var t = Ext.lib.Event.getTarget(e);\r
66872         var cindex = this.view.findCellIndex(t);\r
66873         if(cindex !== false){\r
66874             return this.view.getHeaderCell(cindex);\r
66875         }\r
66876     },\r
66877 \r
66878     nextVisible : function(h){\r
66879         var v = this.view, cm = this.grid.colModel;\r
66880         h = h.nextSibling;\r
66881         while(h){\r
66882             if(!cm.isHidden(v.getCellIndex(h))){\r
66883                 return h;\r
66884             }\r
66885             h = h.nextSibling;\r
66886         }\r
66887         return null;\r
66888     },\r
66889 \r
66890     prevVisible : function(h){\r
66891         var v = this.view, cm = this.grid.colModel;\r
66892         h = h.prevSibling;\r
66893         while(h){\r
66894             if(!cm.isHidden(v.getCellIndex(h))){\r
66895                 return h;\r
66896             }\r
66897             h = h.prevSibling;\r
66898         }\r
66899         return null;\r
66900     },\r
66901 \r
66902     positionIndicator : function(h, n, e){\r
66903         var x = Ext.lib.Event.getPageX(e);\r
66904         var r = Ext.lib.Dom.getRegion(n.firstChild);\r
66905         var px, pt, py = r.top + this.proxyOffsets[1];\r
66906         if((r.right - x) <= (r.right-r.left)/2){\r
66907             px = r.right+this.view.borderWidth;\r
66908             pt = "after";\r
66909         }else{\r
66910             px = r.left;\r
66911             pt = "before";\r
66912         }\r
66913 \r
66914         if(this.grid.colModel.isFixed(this.view.getCellIndex(n))){\r
66915             return false;\r
66916         }\r
66917 \r
66918         px +=  this.proxyOffsets[0];\r
66919         this.proxyTop.setLeftTop(px, py);\r
66920         this.proxyTop.show();\r
66921         if(!this.bottomOffset){\r
66922             this.bottomOffset = this.view.mainHd.getHeight();\r
66923         }\r
66924         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);\r
66925         this.proxyBottom.show();\r
66926         return pt;\r
66927     },\r
66928 \r
66929     onNodeEnter : function(n, dd, e, data){\r
66930         if(data.header != n){\r
66931             this.positionIndicator(data.header, n, e);\r
66932         }\r
66933     },\r
66934 \r
66935     onNodeOver : function(n, dd, e, data){\r
66936         var result = false;\r
66937         if(data.header != n){\r
66938             result = this.positionIndicator(data.header, n, e);\r
66939         }\r
66940         if(!result){\r
66941             this.proxyTop.hide();\r
66942             this.proxyBottom.hide();\r
66943         }\r
66944         return result ? this.dropAllowed : this.dropNotAllowed;\r
66945     },\r
66946 \r
66947     onNodeOut : function(n, dd, e, data){\r
66948         this.proxyTop.hide();\r
66949         this.proxyBottom.hide();\r
66950     },\r
66951 \r
66952     onNodeDrop : function(n, dd, e, data){\r
66953         var h = data.header;\r
66954         if(h != n){\r
66955             var cm = this.grid.colModel;\r
66956             var x = Ext.lib.Event.getPageX(e);\r
66957             var r = Ext.lib.Dom.getRegion(n.firstChild);\r
66958             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";\r
66959             var oldIndex = this.view.getCellIndex(h);\r
66960             var newIndex = this.view.getCellIndex(n);\r
66961             if(pt == "after"){\r
66962                 newIndex++;\r
66963             }\r
66964             if(oldIndex < newIndex){\r
66965                 newIndex--;\r
66966             }\r
66967             cm.moveColumn(oldIndex, newIndex);\r
66968             return true;\r
66969         }\r
66970         return false;\r
66971     }\r
66972 });\r
66973 \r
66974 Ext.grid.GridView.ColumnDragZone = Ext.extend(Ext.grid.HeaderDragZone, {\r
66975     \r
66976     constructor : function(grid, hd){\r
66977         Ext.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);\r
66978         this.proxy.el.addClass('x-grid3-col-dd');\r
66979     },\r
66980     \r
66981     handleMouseDown : function(e){\r
66982     },\r
66983 \r
66984     callHandleMouseDown : function(e){\r
66985         Ext.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);\r
66986     }\r
66987 });// private
66988 // This is a support class used internally by the Grid components
66989 Ext.grid.SplitDragZone = Ext.extend(Ext.dd.DDProxy, {
66990     fly: Ext.Element.fly,
66991     
66992     constructor : function(grid, hd, hd2){
66993         this.grid = grid;
66994         this.view = grid.getView();
66995         this.proxy = this.view.resizeProxy;
66996         Ext.grid.SplitDragZone.superclass.constructor.call(this, hd,
66997             "gridSplitters" + this.grid.getGridEl().id, {
66998             dragElId : Ext.id(this.proxy.dom), resizeFrame:false
66999         });
67000         this.setHandleElId(Ext.id(hd));
67001         this.setOuterHandleElId(Ext.id(hd2));
67002         this.scroll = false;
67003     },
67004
67005     b4StartDrag : function(x, y){
67006         this.view.headersDisabled = true;
67007         this.proxy.setHeight(this.view.mainWrap.getHeight());
67008         var w = this.cm.getColumnWidth(this.cellIndex);
67009         var minw = Math.max(w-this.grid.minColumnWidth, 0);
67010         this.resetConstraints();
67011         this.setXConstraint(minw, 1000);
67012         this.setYConstraint(0, 0);
67013         this.minX = x - minw;
67014         this.maxX = x + 1000;
67015         this.startPos = x;
67016         Ext.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
67017     },
67018
67019
67020     handleMouseDown : function(e){
67021         var ev = Ext.EventObject.setEvent(e);
67022         var t = this.fly(ev.getTarget());
67023         if(t.hasClass("x-grid-split")){
67024             this.cellIndex = this.view.getCellIndex(t.dom);
67025             this.split = t.dom;
67026             this.cm = this.grid.colModel;
67027             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
67028                 Ext.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
67029             }
67030         }
67031     },
67032
67033     endDrag : function(e){
67034         this.view.headersDisabled = false;
67035         var endX = Math.max(this.minX, Ext.lib.Event.getPageX(e));
67036         var diff = endX - this.startPos;
67037         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
67038     },
67039
67040     autoOffset : function(){
67041         this.setDelta(0,0);
67042     }
67043 });/**
67044  * @class Ext.grid.GridDragZone
67045  * @extends Ext.dd.DragZone
67046  * <p>A customized implementation of a {@link Ext.dd.DragZone DragZone} which provides default implementations of two of the
67047  * template methods of DragZone to enable dragging of the selected rows of a GridPanel.</p>
67048  * <p>A cooperating {@link Ext.dd.DropZone DropZone} must be created who's template method implementations of
67049  * {@link Ext.dd.DropZone#onNodeEnter onNodeEnter}, {@link Ext.dd.DropZone#onNodeOver onNodeOver},
67050  * {@link Ext.dd.DropZone#onNodeOut onNodeOut} and {@link Ext.dd.DropZone#onNodeDrop onNodeDrop}</p> are able
67051  * to process the {@link #getDragData data} which is provided.
67052  */
67053 Ext.grid.GridDragZone = function(grid, config){
67054     this.view = grid.getView();
67055     Ext.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
67056     this.scroll = false;
67057     this.grid = grid;
67058     this.ddel = document.createElement('div');
67059     this.ddel.className = 'x-grid-dd-wrap';
67060 };
67061
67062 Ext.extend(Ext.grid.GridDragZone, Ext.dd.DragZone, {
67063     ddGroup : "GridDD",
67064
67065     /**
67066      * <p>The provided implementation of the getDragData method which collects the data to be dragged from the GridPanel on mousedown.</p>
67067      * <p>This data is available for processing in the {@link Ext.dd.DropZone#onNodeEnter onNodeEnter}, {@link Ext.dd.DropZone#onNodeOver onNodeOver},
67068      * {@link Ext.dd.DropZone#onNodeOut onNodeOut} and {@link Ext.dd.DropZone#onNodeDrop onNodeDrop} methods of a cooperating {@link Ext.dd.DropZone DropZone}.</p>
67069      * <p>The data object contains the following properties:<ul>
67070      * <li><b>grid</b> : Ext.Grid.GridPanel<div class="sub-desc">The GridPanel from which the data is being dragged.</div></li>
67071      * <li><b>ddel</b> : htmlElement<div class="sub-desc">An htmlElement which provides the "picture" of the data being dragged.</div></li>
67072      * <li><b>rowIndex</b> : Number<div class="sub-desc">The index of the row which receieved the mousedown gesture which triggered the drag.</div></li>
67073      * <li><b>selections</b> : Array<div class="sub-desc">An Array of the selected Records which are being dragged from the GridPanel.</div></li>
67074      * </ul></p>
67075      */
67076     getDragData : function(e){
67077         var t = Ext.lib.Event.getTarget(e);
67078         var rowIndex = this.view.findRowIndex(t);
67079         if(rowIndex !== false){
67080             var sm = this.grid.selModel;
67081             if(!sm.isSelected(rowIndex) || e.hasModifier()){
67082                 sm.handleMouseDown(this.grid, rowIndex, e);
67083             }
67084             return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};
67085         }
67086         return false;
67087     },
67088
67089     /**
67090      * <p>The provided implementation of the onInitDrag method. Sets the <tt>innerHTML</tt> of the drag proxy which provides the "picture"
67091      * of the data being dragged.</p>
67092      * <p>The <tt>innerHTML</tt> data is found by calling the owning GridPanel's {@link Ext.grid.GridPanel#getDragDropText getDragDropText}.</p>
67093      */
67094     onInitDrag : function(e){
67095         var data = this.dragData;
67096         this.ddel.innerHTML = this.grid.getDragDropText();
67097         this.proxy.update(this.ddel);
67098         // fire start drag?
67099     },
67100
67101     /**
67102      * An empty immplementation. Implement this to provide behaviour after a repair of an invalid drop. An implementation might highlight
67103      * the selected rows to show that they have not been dragged.
67104      */
67105     afterRepair : function(){
67106         this.dragging = false;
67107     },
67108
67109     /**
67110      * <p>An empty implementation. Implement this to provide coordinates for the drag proxy to slide back to after an invalid drop.</p>
67111      * <p>Called before a repair of an invalid drop to get the XY to animate to.</p>
67112      * @param {EventObject} e The mouse up event
67113      * @return {Array} The xy location (e.g. [100, 200])
67114      */
67115     getRepairXY : function(e, data){
67116         return false;
67117     },
67118
67119     onEndDrag : function(data, e){
67120         // fire end drag?
67121     },
67122
67123     onValidDrop : function(dd, e, id){
67124         // fire drag drop?
67125         this.hideProxy();
67126     },
67127
67128     beforeInvalidDrop : function(e, id){
67129
67130     }
67131 });
67132 /**
67133  * @class Ext.grid.ColumnModel
67134  * @extends Ext.util.Observable
67135  * <p>After the data has been read into the client side cache (<b>{@link Ext.data.Store Store}</b>),
67136  * the ColumnModel is used to configure how and what parts of that data will be displayed in the
67137  * vertical slices (columns) of the grid. The Ext.grid.ColumnModel Class is the default implementation
67138  * of a ColumnModel used by implentations of {@link Ext.grid.GridPanel GridPanel}.</p>
67139  * <p>Data is mapped into the store's records and then indexed into the ColumnModel using the
67140  * <tt>{@link Ext.grid.Column#dataIndex dataIndex}</tt>:</p>
67141  * <pre><code>
67142 {data source} == mapping ==> {data store} == <b><tt>{@link Ext.grid.Column#dataIndex dataIndex}</tt></b> ==> {ColumnModel}
67143  * </code></pre>
67144  * <p>Each {@link Ext.grid.Column Column} in the grid's ColumnModel is configured with a
67145  * <tt>{@link Ext.grid.Column#dataIndex dataIndex}</tt> to specify how the data within
67146  * each record in the store is indexed into the ColumnModel.</p>
67147  * <p>There are two ways to initialize the ColumnModel class:</p>
67148  * <p><u>Initialization Method 1: an Array</u></p>
67149 <pre><code>
67150  var colModel = new Ext.grid.ColumnModel([
67151     { header: "Ticker", width: 60, sortable: true},
67152     { header: "Company Name", width: 150, sortable: true, id: 'company'},
67153     { header: "Market Cap.", width: 100, sortable: true},
67154     { header: "$ Sales", width: 100, sortable: true, renderer: money},
67155     { header: "Employees", width: 100, sortable: true, resizable: false}
67156  ]);
67157  </code></pre>
67158  * <p>The ColumnModel may be initialized with an Array of {@link Ext.grid.Column} column configuration
67159  * objects to define the initial layout / display of the columns in the Grid. The order of each
67160  * {@link Ext.grid.Column} column configuration object within the specified Array defines the initial
67161  * order of the column display.  A Column's display may be initially hidden using the
67162  * <tt>{@link Ext.grid.Column#hidden hidden}</tt></b> config property (and then shown using the column
67163  * header menu).  Fields that are not included in the ColumnModel will not be displayable at all.</p>
67164  * <p>How each column in the grid correlates (maps) to the {@link Ext.data.Record} field in the
67165  * {@link Ext.data.Store Store} the column draws its data from is configured through the
67166  * <b><tt>{@link Ext.grid.Column#dataIndex dataIndex}</tt></b>.  If the
67167  * <b><tt>{@link Ext.grid.Column#dataIndex dataIndex}</tt></b> is not explicitly defined (as shown in the
67168  * example above) it will use the column configuration's index in the Array as the index.</p>
67169  * <p>See <b><tt>{@link Ext.grid.Column}</tt></b> for additional configuration options for each column.</p>
67170  * <p><u>Initialization Method 2: an Object</u></p>
67171  * <p>In order to use configuration options from <tt>Ext.grid.ColumnModel</tt>, an Object may be used to
67172  * initialize the ColumnModel.  The column configuration Array will be specified in the <tt><b>{@link #columns}</b></tt>
67173  * config property. The <tt><b>{@link #defaults}</b></tt> config property can be used to apply defaults
67174  * for all columns, e.g.:</p><pre><code>
67175  var colModel = new Ext.grid.ColumnModel({
67176     columns: [
67177         { header: "Ticker", width: 60, menuDisabled: false},
67178         { header: "Company Name", width: 150, id: 'company'},
67179         { header: "Market Cap."},
67180         { header: "$ Sales", renderer: money},
67181         { header: "Employees", resizable: false}
67182     ],
67183     defaults: {
67184         sortable: true,
67185         menuDisabled: true,
67186         width: 100
67187     },
67188     listeners: {
67189         {@link #hiddenchange}: function(cm, colIndex, hidden) {
67190             saveConfig(colIndex, hidden);
67191         }
67192     }
67193 });
67194  </code></pre>
67195  * <p>In both examples above, the ability to apply a CSS class to all cells in a column (including the
67196  * header) is demonstrated through the use of the <b><tt>{@link Ext.grid.Column#id id}</tt></b> config
67197  * option. This column could be styled by including the following css:</p><pre><code>
67198  //add this css *after* the core css is loaded
67199 .x-grid3-td-company {
67200     color: red; // entire column will have red font
67201 }
67202 // modify the header row only, adding an icon to the column header
67203 .x-grid3-hd-company {
67204     background: transparent
67205         url(../../resources/images/icons/silk/building.png)
67206         no-repeat 3px 3px ! important;
67207         padding-left:20px;
67208 }
67209  </code></pre>
67210  * Note that the "Company Name" column could be specified as the
67211  * <b><tt>{@link Ext.grid.GridPanel}.{@link Ext.grid.GridPanel#autoExpandColumn autoExpandColumn}</tt></b>.
67212  * @constructor
67213  * @param {Mixed} config Specify either an Array of {@link Ext.grid.Column} configuration objects or specify
67214  * a configuration Object (see introductory section discussion utilizing Initialization Method 2 above).
67215  */
67216 Ext.grid.ColumnModel = Ext.extend(Ext.util.Observable, {
67217     /**
67218      * @cfg {Number} defaultWidth (optional) The width of columns which have no <tt>{@link #width}</tt>
67219      * specified (defaults to <tt>100</tt>).  This property shall preferably be configured through the
67220      * <tt><b>{@link #defaults}</b></tt> config property.
67221      */
67222     defaultWidth: 100,
67223     /**
67224      * @cfg {Boolean} defaultSortable (optional) Default sortable of columns which have no
67225      * sortable specified (defaults to <tt>false</tt>).  This property shall preferably be configured
67226      * through the <tt><b>{@link #defaults}</b></tt> config property.
67227      */
67228     defaultSortable: false,
67229     /**
67230      * @cfg {Array} columns An Array of object literals.  The config options defined by
67231      * <b>{@link Ext.grid.Column}</b> are the options which may appear in the object literal for each
67232      * individual column definition.
67233      */
67234     /**
67235      * @cfg {Object} defaults Object literal which will be used to apply {@link Ext.grid.Column}
67236      * configuration options to all <tt><b>{@link #columns}</b></tt>.  Configuration options specified with
67237      * individual {@link Ext.grid.Column column} configs will supersede these <tt><b>{@link #defaults}</b></tt>.
67238      */
67239     
67240     constructor : function(config){
67241         /**
67242              * An Array of {@link Ext.grid.Column Column definition} objects representing the configuration
67243              * of this ColumnModel.  See {@link Ext.grid.Column} for the configuration properties that may
67244              * be specified.
67245              * @property config
67246              * @type Array
67247              */
67248             if(config.columns){
67249                 Ext.apply(this, config);
67250                 this.setConfig(config.columns, true);
67251             }else{
67252                 this.setConfig(config, true);
67253             }
67254             this.addEvents(
67255                 /**
67256                  * @event widthchange
67257                  * Fires when the width of a column is programmaticially changed using
67258                  * <code>{@link #setColumnWidth}</code>.
67259                  * Note internal resizing suppresses the event from firing. See also
67260                  * {@link Ext.grid.GridPanel}.<code>{@link #columnresize}</code>.
67261                  * @param {ColumnModel} this
67262                  * @param {Number} columnIndex The column index
67263                  * @param {Number} newWidth The new width
67264                  */
67265                 "widthchange",
67266                 /**
67267                  * @event headerchange
67268                  * Fires when the text of a header changes.
67269                  * @param {ColumnModel} this
67270                  * @param {Number} columnIndex The column index
67271                  * @param {String} newText The new header text
67272                  */
67273                 "headerchange",
67274                 /**
67275                  * @event hiddenchange
67276                  * Fires when a column is hidden or "unhidden".
67277                  * @param {ColumnModel} this
67278                  * @param {Number} columnIndex The column index
67279                  * @param {Boolean} hidden true if hidden, false otherwise
67280                  */
67281                 "hiddenchange",
67282                 /**
67283                  * @event columnmoved
67284                  * Fires when a column is moved.
67285                  * @param {ColumnModel} this
67286                  * @param {Number} oldIndex
67287                  * @param {Number} newIndex
67288                  */
67289                 "columnmoved",
67290                 /**
67291                  * @event configchange
67292                  * Fires when the configuration is changed
67293                  * @param {ColumnModel} this
67294                  */
67295                 "configchange"
67296             );
67297             Ext.grid.ColumnModel.superclass.constructor.call(this);
67298     },
67299
67300     /**
67301      * Returns the id of the column at the specified index.
67302      * @param {Number} index The column index
67303      * @return {String} the id
67304      */
67305     getColumnId : function(index){
67306         return this.config[index].id;
67307     },
67308
67309     getColumnAt : function(index){
67310         return this.config[index];
67311     },
67312
67313     /**
67314      * <p>Reconfigures this column model according to the passed Array of column definition objects.
67315      * For a description of the individual properties of a column definition object, see the
67316      * <a href="#Ext.grid.ColumnModel-configs">Config Options</a>.</p>
67317      * <p>Causes the {@link #configchange} event to be fired. A {@link Ext.grid.GridPanel GridPanel}
67318      * using this ColumnModel will listen for this event and refresh its UI automatically.</p>
67319      * @param {Array} config Array of Column definition objects.
67320      * @param {Boolean} initial Specify <tt>true</tt> to bypass cleanup which deletes the <tt>totalWidth</tt>
67321      * and destroys existing editors.
67322      */
67323     setConfig : function(config, initial){
67324         var i, c, len;
67325         if(!initial){ // cleanup
67326             delete this.totalWidth;
67327             for(i = 0, len = this.config.length; i < len; i++){
67328                 this.config[i].destroy();
67329             }
67330         }
67331
67332         // backward compatibility
67333         this.defaults = Ext.apply({
67334             width: this.defaultWidth,
67335             sortable: this.defaultSortable
67336         }, this.defaults);
67337
67338         this.config = config;
67339         this.lookup = {};
67340
67341         for(i = 0, len = config.length; i < len; i++){
67342             c = Ext.applyIf(config[i], this.defaults);
67343             // if no id, create one using column's ordinal position
67344             if(Ext.isEmpty(c.id)){
67345                 c.id = i;
67346             }
67347             if(!c.isColumn){
67348                 var Cls = Ext.grid.Column.types[c.xtype || 'gridcolumn'];
67349                 c = new Cls(c);
67350                 config[i] = c;
67351             }
67352             this.lookup[c.id] = c;
67353         }
67354         if(!initial){
67355             this.fireEvent('configchange', this);
67356         }
67357     },
67358
67359     /**
67360      * Returns the column for a specified id.
67361      * @param {String} id The column id
67362      * @return {Object} the column
67363      */
67364     getColumnById : function(id){
67365         return this.lookup[id];
67366     },
67367
67368     /**
67369      * Returns the index for a specified column id.
67370      * @param {String} id The column id
67371      * @return {Number} the index, or -1 if not found
67372      */
67373     getIndexById : function(id){
67374         for(var i = 0, len = this.config.length; i < len; i++){
67375             if(this.config[i].id == id){
67376                 return i;
67377             }
67378         }
67379         return -1;
67380     },
67381
67382     /**
67383      * Moves a column from one position to another.
67384      * @param {Number} oldIndex The index of the column to move.
67385      * @param {Number} newIndex The position at which to reinsert the coolumn.
67386      */
67387     moveColumn : function(oldIndex, newIndex){
67388         var c = this.config[oldIndex];
67389         this.config.splice(oldIndex, 1);
67390         this.config.splice(newIndex, 0, c);
67391         this.dataMap = null;
67392         this.fireEvent("columnmoved", this, oldIndex, newIndex);
67393     },
67394
67395     /**
67396      * Returns the number of columns.
67397      * @param {Boolean} visibleOnly Optional. Pass as true to only include visible columns.
67398      * @return {Number}
67399      */
67400     getColumnCount : function(visibleOnly){
67401         if(visibleOnly === true){
67402             var c = 0;
67403             for(var i = 0, len = this.config.length; i < len; i++){
67404                 if(!this.isHidden(i)){
67405                     c++;
67406                 }
67407             }
67408             return c;
67409         }
67410         return this.config.length;
67411     },
67412
67413     /**
67414      * Returns the column configs that return true by the passed function that is called
67415      * with (columnConfig, index)
67416 <pre><code>
67417 // returns an array of column config objects for all hidden columns
67418 var columns = grid.getColumnModel().getColumnsBy(function(c){
67419   return c.hidden;
67420 });
67421 </code></pre>
67422      * @param {Function} fn A function which, when passed a {@link Ext.grid.Column Column} object, must
67423      * return <code>true</code> if the column is to be included in the returned Array.
67424      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function
67425      * is executed. Defaults to this ColumnModel.
67426      * @return {Array} result
67427      */
67428     getColumnsBy : function(fn, scope){
67429         var r = [];
67430         for(var i = 0, len = this.config.length; i < len; i++){
67431             var c = this.config[i];
67432             if(fn.call(scope||this, c, i) === true){
67433                 r[r.length] = c;
67434             }
67435         }
67436         return r;
67437     },
67438
67439     /**
67440      * Returns true if the specified column is sortable.
67441      * @param {Number} col The column index
67442      * @return {Boolean}
67443      */
67444     isSortable : function(col){
67445         return !!this.config[col].sortable;
67446     },
67447
67448     /**
67449      * Returns true if the specified column menu is disabled.
67450      * @param {Number} col The column index
67451      * @return {Boolean}
67452      */
67453     isMenuDisabled : function(col){
67454         return !!this.config[col].menuDisabled;
67455     },
67456
67457     /**
67458      * Returns the rendering (formatting) function defined for the column.
67459      * @param {Number} col The column index.
67460      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
67461      */
67462     getRenderer : function(col){
67463         if(!this.config[col].renderer){
67464             return Ext.grid.ColumnModel.defaultRenderer;
67465         }
67466         return this.config[col].renderer;
67467     },
67468     
67469     getRendererScope : function(col){
67470         return this.config[col].scope;
67471     },
67472
67473     /**
67474      * Sets the rendering (formatting) function for a column.  See {@link Ext.util.Format} for some
67475      * default formatting functions.
67476      * @param {Number} col The column index
67477      * @param {Function} fn The function to use to process the cell's raw data
67478      * to return HTML markup for the grid view. The render function is called with
67479      * the following parameters:<ul>
67480      * <li><b>value</b> : Object<p class="sub-desc">The data value for the cell.</p></li>
67481      * <li><b>metadata</b> : Object<p class="sub-desc">An object in which you may set the following attributes:<ul>
67482      * <li><b>css</b> : String<p class="sub-desc">A CSS class name to add to the cell's TD element.</p></li>
67483      * <li><b>attr</b> : String<p class="sub-desc">An HTML attribute definition string to apply to the data container element <i>within</i> the table cell
67484      * (e.g. 'style="color:red;"').</p></li></ul></p></li>
67485      * <li><b>record</b> : Ext.data.record<p class="sub-desc">The {@link Ext.data.Record} from which the data was extracted.</p></li>
67486      * <li><b>rowIndex</b> : Number<p class="sub-desc">Row index</p></li>
67487      * <li><b>colIndex</b> : Number<p class="sub-desc">Column index</p></li>
67488      * <li><b>store</b> : Ext.data.Store<p class="sub-desc">The {@link Ext.data.Store} object from which the Record was extracted.</p></li></ul>
67489      */
67490     setRenderer : function(col, fn){
67491         this.config[col].renderer = fn;
67492     },
67493
67494     /**
67495      * Returns the width for the specified column.
67496      * @param {Number} col The column index
67497      * @return {Number}
67498      */
67499     getColumnWidth : function(col){
67500         return this.config[col].width;
67501     },
67502
67503     /**
67504      * Sets the width for a column.
67505      * @param {Number} col The column index
67506      * @param {Number} width The new width
67507      * @param {Boolean} suppressEvent True to suppress firing the <code>{@link #widthchange}</code>
67508      * event. Defaults to false.
67509      */
67510     setColumnWidth : function(col, width, suppressEvent){
67511         this.config[col].width = width;
67512         this.totalWidth = null;
67513         if(!suppressEvent){
67514              this.fireEvent("widthchange", this, col, width);
67515         }
67516     },
67517
67518     /**
67519      * Returns the total width of all columns.
67520      * @param {Boolean} includeHidden True to include hidden column widths
67521      * @return {Number}
67522      */
67523     getTotalWidth : function(includeHidden){
67524         if(!this.totalWidth){
67525             this.totalWidth = 0;
67526             for(var i = 0, len = this.config.length; i < len; i++){
67527                 if(includeHidden || !this.isHidden(i)){
67528                     this.totalWidth += this.getColumnWidth(i);
67529                 }
67530             }
67531         }
67532         return this.totalWidth;
67533     },
67534
67535     /**
67536      * Returns the header for the specified column.
67537      * @param {Number} col The column index
67538      * @return {String}
67539      */
67540     getColumnHeader : function(col){
67541         return this.config[col].header;
67542     },
67543
67544     /**
67545      * Sets the header for a column.
67546      * @param {Number} col The column index
67547      * @param {String} header The new header
67548      */
67549     setColumnHeader : function(col, header){
67550         this.config[col].header = header;
67551         this.fireEvent("headerchange", this, col, header);
67552     },
67553
67554     /**
67555      * Returns the tooltip for the specified column.
67556      * @param {Number} col The column index
67557      * @return {String}
67558      */
67559     getColumnTooltip : function(col){
67560             return this.config[col].tooltip;
67561     },
67562     /**
67563      * Sets the tooltip for a column.
67564      * @param {Number} col The column index
67565      * @param {String} tooltip The new tooltip
67566      */
67567     setColumnTooltip : function(col, tooltip){
67568             this.config[col].tooltip = tooltip;
67569     },
67570
67571     /**
67572      * Returns the dataIndex for the specified column.
67573 <pre><code>
67574 // Get field name for the column
67575 var fieldName = grid.getColumnModel().getDataIndex(columnIndex);
67576 </code></pre>
67577      * @param {Number} col The column index
67578      * @return {String} The column's dataIndex
67579      */
67580     getDataIndex : function(col){
67581         return this.config[col].dataIndex;
67582     },
67583
67584     /**
67585      * Sets the dataIndex for a column.
67586      * @param {Number} col The column index
67587      * @param {String} dataIndex The new dataIndex
67588      */
67589     setDataIndex : function(col, dataIndex){
67590         this.config[col].dataIndex = dataIndex;
67591     },
67592
67593     /**
67594      * Finds the index of the first matching column for the given dataIndex.
67595      * @param {String} col The dataIndex to find
67596      * @return {Number} The column index, or -1 if no match was found
67597      */
67598     findColumnIndex : function(dataIndex){
67599         var c = this.config;
67600         for(var i = 0, len = c.length; i < len; i++){
67601             if(c[i].dataIndex == dataIndex){
67602                 return i;
67603             }
67604         }
67605         return -1;
67606     },
67607
67608     /**
67609      * Returns true if the cell is editable.
67610 <pre><code>
67611 var store = new Ext.data.Store({...});
67612 var colModel = new Ext.grid.ColumnModel({
67613   columns: [...],
67614   isCellEditable: function(col, row) {
67615     var record = store.getAt(row);
67616     if (record.get('readonly')) { // replace with your condition
67617       return false;
67618     }
67619     return Ext.grid.ColumnModel.prototype.isCellEditable.call(this, col, row);
67620   }
67621 });
67622 var grid = new Ext.grid.GridPanel({
67623   store: store,
67624   colModel: colModel,
67625   ...
67626 });
67627 </code></pre>
67628      * @param {Number} colIndex The column index
67629      * @param {Number} rowIndex The row index
67630      * @return {Boolean}
67631      */
67632     isCellEditable : function(colIndex, rowIndex){
67633         var c = this.config[colIndex],
67634             ed = c.editable;
67635             
67636         //force boolean
67637         return !!(ed || (!Ext.isDefined(ed) && c.editor));
67638     },
67639
67640     /**
67641      * Returns the editor defined for the cell/column.
67642      * @param {Number} colIndex The column index
67643      * @param {Number} rowIndex The row index
67644      * @return {Ext.Editor} The {@link Ext.Editor Editor} that was created to wrap
67645      * the {@link Ext.form.Field Field} used to edit the cell.
67646      */
67647     getCellEditor : function(colIndex, rowIndex){
67648         return this.config[colIndex].getCellEditor(rowIndex);
67649     },
67650
67651     /**
67652      * Sets if a column is editable.
67653      * @param {Number} col The column index
67654      * @param {Boolean} editable True if the column is editable
67655      */
67656     setEditable : function(col, editable){
67657         this.config[col].editable = editable;
67658     },
67659
67660     /**
67661      * Returns <tt>true</tt> if the column is <code>{@link Ext.grid.Column#hidden hidden}</code>,
67662      * <tt>false</tt> otherwise.
67663      * @param {Number} colIndex The column index
67664      * @return {Boolean}
67665      */
67666     isHidden : function(colIndex){
67667         return !!this.config[colIndex].hidden; // ensure returns boolean
67668     },
67669
67670     /**
67671      * Returns <tt>true</tt> if the column is <code>{@link Ext.grid.Column#fixed fixed}</code>,
67672      * <tt>false</tt> otherwise.
67673      * @param {Number} colIndex The column index
67674      * @return {Boolean}
67675      */
67676     isFixed : function(colIndex){
67677         return !!this.config[colIndex].fixed;
67678     },
67679
67680     /**
67681      * Returns true if the column can be resized
67682      * @return {Boolean}
67683      */
67684     isResizable : function(colIndex){
67685         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
67686     },
67687     /**
67688      * Sets if a column is hidden.
67689 <pre><code>
67690 myGrid.getColumnModel().setHidden(0, true); // hide column 0 (0 = the first column).
67691 </code></pre>
67692      * @param {Number} colIndex The column index
67693      * @param {Boolean} hidden True if the column is hidden
67694      */
67695     setHidden : function(colIndex, hidden){
67696         var c = this.config[colIndex];
67697         if(c.hidden !== hidden){
67698             c.hidden = hidden;
67699             this.totalWidth = null;
67700             this.fireEvent("hiddenchange", this, colIndex, hidden);
67701         }
67702     },
67703
67704     /**
67705      * Sets the editor for a column and destroys the prior editor.
67706      * @param {Number} col The column index
67707      * @param {Object} editor The editor object
67708      */
67709     setEditor : function(col, editor){
67710         this.config[col].setEditor(editor);
67711     },
67712
67713     /**
67714      * Destroys this column model by purging any event listeners, and removing any editors.
67715      */
67716     destroy : function(){
67717         for(var i = 0, len = this.config.length; i < len; i++){
67718             this.config[i].destroy();
67719         }
67720         this.purgeListeners();
67721     }
67722 });
67723
67724 // private
67725 Ext.grid.ColumnModel.defaultRenderer = function(value){
67726     if(typeof value == "string" && value.length < 1){
67727         return "&#160;";
67728     }
67729     return value;
67730 };/**\r
67731  * @class Ext.grid.AbstractSelectionModel\r
67732  * @extends Ext.util.Observable\r
67733  * Abstract base class for grid SelectionModels.  It provides the interface that should be\r
67734  * implemented by descendant classes.  This class should not be directly instantiated.\r
67735  * @constructor\r
67736  */\r
67737 Ext.grid.AbstractSelectionModel = Ext.extend(Ext.util.Observable,  {\r
67738     /**\r
67739      * The GridPanel for which this SelectionModel is handling selection. Read-only.\r
67740      * @type Object\r
67741      * @property grid\r
67742      */\r
67743     \r
67744     constructor : function(){\r
67745         this.locked = false;\r
67746         Ext.grid.AbstractSelectionModel.superclass.constructor.call(this);\r
67747     },\r
67748 \r
67749     /** @ignore Called by the grid automatically. Do not call directly. */\r
67750     init : function(grid){\r
67751         this.grid = grid;\r
67752         this.initEvents();\r
67753     },\r
67754 \r
67755     /**\r
67756      * Locks the selections.\r
67757      */\r
67758     lock : function(){\r
67759         this.locked = true;\r
67760     },\r
67761 \r
67762     /**\r
67763      * Unlocks the selections.\r
67764      */\r
67765     unlock : function(){\r
67766         this.locked = false;\r
67767     },\r
67768 \r
67769     /**\r
67770      * Returns true if the selections are locked.\r
67771      * @return {Boolean}\r
67772      */\r
67773     isLocked : function(){\r
67774         return this.locked;\r
67775     },\r
67776     \r
67777     destroy: function(){\r
67778         this.purgeListeners();\r
67779     }\r
67780 });/**
67781  * @class Ext.grid.RowSelectionModel
67782  * @extends Ext.grid.AbstractSelectionModel
67783  * The default SelectionModel used by {@link Ext.grid.GridPanel}.
67784  * It supports multiple selections and keyboard selection/navigation. The objects stored
67785  * as selections and returned by {@link #getSelected}, and {@link #getSelections} are
67786  * the {@link Ext.data.Record Record}s which provide the data for the selected rows.
67787  * @constructor
67788  * @param {Object} config
67789  */
67790 Ext.grid.RowSelectionModel = Ext.extend(Ext.grid.AbstractSelectionModel,  {
67791     /**
67792      * @cfg {Boolean} singleSelect
67793      * <tt>true</tt> to allow selection of only one row at a time (defaults to <tt>false</tt>
67794      * allowing multiple selections)
67795      */
67796     singleSelect : false,
67797     
67798     constructor : function(config){
67799         Ext.apply(this, config);
67800         this.selections = new Ext.util.MixedCollection(false, function(o){
67801             return o.id;
67802         });
67803
67804         this.last = false;
67805         this.lastActive = false;
67806
67807         this.addEvents(
67808                 /**
67809                  * @event selectionchange
67810                  * Fires when the selection changes
67811                  * @param {SelectionModel} this
67812                  */
67813                 'selectionchange',
67814                 /**
67815                  * @event beforerowselect
67816                  * Fires before a row is selected, return false to cancel the selection.
67817                  * @param {SelectionModel} this
67818                  * @param {Number} rowIndex The index to be selected
67819                  * @param {Boolean} keepExisting False if other selections will be cleared
67820                  * @param {Record} record The record to be selected
67821                  */
67822                 'beforerowselect',
67823                 /**
67824                  * @event rowselect
67825                  * Fires when a row is selected.
67826                  * @param {SelectionModel} this
67827                  * @param {Number} rowIndex The selected index
67828                  * @param {Ext.data.Record} r The selected record
67829                  */
67830                 'rowselect',
67831                 /**
67832                  * @event rowdeselect
67833                  * Fires when a row is deselected.  To prevent deselection
67834                  * {@link Ext.grid.AbstractSelectionModel#lock lock the selections}. 
67835                  * @param {SelectionModel} this
67836                  * @param {Number} rowIndex
67837                  * @param {Record} record
67838                  */
67839                 'rowdeselect'
67840         );
67841         Ext.grid.RowSelectionModel.superclass.constructor.call(this);
67842     },
67843
67844     /**
67845      * @cfg {Boolean} moveEditorOnEnter
67846      * <tt>false</tt> to turn off moving the editor to the next row down when the enter key is pressed
67847      * or the next row up when shift + enter keys are pressed.
67848      */
67849     // private
67850     initEvents : function(){
67851
67852         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
67853             this.grid.on('rowmousedown', this.handleMouseDown, this);
67854         }
67855
67856         this.rowNav = new Ext.KeyNav(this.grid.getGridEl(), {
67857             'up' : function(e){
67858                 if(!e.shiftKey || this.singleSelect){
67859                     this.selectPrevious(false);
67860                 }else if(this.last !== false && this.lastActive !== false){
67861                     var last = this.last;
67862                     this.selectRange(this.last,  this.lastActive-1);
67863                     this.grid.getView().focusRow(this.lastActive);
67864                     if(last !== false){
67865                         this.last = last;
67866                     }
67867                 }else{
67868                     this.selectFirstRow();
67869                 }
67870             },
67871             'down' : function(e){
67872                 if(!e.shiftKey || this.singleSelect){
67873                     this.selectNext(false);
67874                 }else if(this.last !== false && this.lastActive !== false){
67875                     var last = this.last;
67876                     this.selectRange(this.last,  this.lastActive+1);
67877                     this.grid.getView().focusRow(this.lastActive);
67878                     if(last !== false){
67879                         this.last = last;
67880                     }
67881                 }else{
67882                     this.selectFirstRow();
67883                 }
67884             },
67885             scope: this
67886         });
67887
67888         this.grid.getView().on({
67889             scope: this,
67890             refresh: this.onRefresh,
67891             rowupdated: this.onRowUpdated,
67892             rowremoved: this.onRemove
67893         });
67894     },
67895
67896     // private
67897     onRefresh : function(){
67898         var ds = this.grid.store, index;
67899         var s = this.getSelections();
67900         this.clearSelections(true);
67901         for(var i = 0, len = s.length; i < len; i++){
67902             var r = s[i];
67903             if((index = ds.indexOfId(r.id)) != -1){
67904                 this.selectRow(index, true);
67905             }
67906         }
67907         if(s.length != this.selections.getCount()){
67908             this.fireEvent('selectionchange', this);
67909         }
67910     },
67911
67912     // private
67913     onRemove : function(v, index, r){
67914         if(this.selections.remove(r) !== false){
67915             this.fireEvent('selectionchange', this);
67916         }
67917     },
67918
67919     // private
67920     onRowUpdated : function(v, index, r){
67921         if(this.isSelected(r)){
67922             v.onRowSelect(index);
67923         }
67924     },
67925
67926     /**
67927      * Select records.
67928      * @param {Array} records The records to select
67929      * @param {Boolean} keepExisting (optional) <tt>true</tt> to keep existing selections
67930      */
67931     selectRecords : function(records, keepExisting){
67932         if(!keepExisting){
67933             this.clearSelections();
67934         }
67935         var ds = this.grid.store;
67936         for(var i = 0, len = records.length; i < len; i++){
67937             this.selectRow(ds.indexOf(records[i]), true);
67938         }
67939     },
67940
67941     /**
67942      * Gets the number of selected rows.
67943      * @return {Number}
67944      */
67945     getCount : function(){
67946         return this.selections.length;
67947     },
67948
67949     /**
67950      * Selects the first row in the grid.
67951      */
67952     selectFirstRow : function(){
67953         this.selectRow(0);
67954     },
67955
67956     /**
67957      * Select the last row.
67958      * @param {Boolean} keepExisting (optional) <tt>true</tt> to keep existing selections
67959      */
67960     selectLastRow : function(keepExisting){
67961         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
67962     },
67963
67964     /**
67965      * Selects the row immediately following the last selected row.
67966      * @param {Boolean} keepExisting (optional) <tt>true</tt> to keep existing selections
67967      * @return {Boolean} <tt>true</tt> if there is a next row, else <tt>false</tt>
67968      */
67969     selectNext : function(keepExisting){
67970         if(this.hasNext()){
67971             this.selectRow(this.last+1, keepExisting);
67972             this.grid.getView().focusRow(this.last);
67973             return true;
67974         }
67975         return false;
67976     },
67977
67978     /**
67979      * Selects the row that precedes the last selected row.
67980      * @param {Boolean} keepExisting (optional) <tt>true</tt> to keep existing selections
67981      * @return {Boolean} <tt>true</tt> if there is a previous row, else <tt>false</tt>
67982      */
67983     selectPrevious : function(keepExisting){
67984         if(this.hasPrevious()){
67985             this.selectRow(this.last-1, keepExisting);
67986             this.grid.getView().focusRow(this.last);
67987             return true;
67988         }
67989         return false;
67990     },
67991
67992     /**
67993      * Returns true if there is a next record to select
67994      * @return {Boolean}
67995      */
67996     hasNext : function(){
67997         return this.last !== false && (this.last+1) < this.grid.store.getCount();
67998     },
67999
68000     /**
68001      * Returns true if there is a previous record to select
68002      * @return {Boolean}
68003      */
68004     hasPrevious : function(){
68005         return !!this.last;
68006     },
68007
68008
68009     /**
68010      * Returns the selected records
68011      * @return {Array} Array of selected records
68012      */
68013     getSelections : function(){
68014         return [].concat(this.selections.items);
68015     },
68016
68017     /**
68018      * Returns the first selected record.
68019      * @return {Record}
68020      */
68021     getSelected : function(){
68022         return this.selections.itemAt(0);
68023     },
68024
68025     /**
68026      * Calls the passed function with each selection. If the function returns
68027      * <tt>false</tt>, iteration is stopped and this function returns
68028      * <tt>false</tt>. Otherwise it returns <tt>true</tt>.
68029      * @param {Function} fn The function to call upon each iteration. It is passed the selected {@link Ext.data.Record Record}.
68030      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to this RowSelectionModel.
68031      * @return {Boolean} true if all selections were iterated
68032      */
68033     each : function(fn, scope){
68034         var s = this.getSelections();
68035         for(var i = 0, len = s.length; i < len; i++){
68036             if(fn.call(scope || this, s[i], i) === false){
68037                 return false;
68038             }
68039         }
68040         return true;
68041     },
68042
68043     /**
68044      * Clears all selections if the selection model
68045      * {@link Ext.grid.AbstractSelectionModel#isLocked is not locked}.
68046      * @param {Boolean} fast (optional) <tt>true</tt> to bypass the
68047      * conditional checks and events described in {@link #deselectRow}.
68048      */
68049     clearSelections : function(fast){
68050         if(this.isLocked()){
68051             return;
68052         }
68053         if(fast !== true){
68054             var ds = this.grid.store;
68055             var s = this.selections;
68056             s.each(function(r){
68057                 this.deselectRow(ds.indexOfId(r.id));
68058             }, this);
68059             s.clear();
68060         }else{
68061             this.selections.clear();
68062         }
68063         this.last = false;
68064     },
68065
68066
68067     /**
68068      * Selects all rows if the selection model
68069      * {@link Ext.grid.AbstractSelectionModel#isLocked is not locked}. 
68070      */
68071     selectAll : function(){
68072         if(this.isLocked()){
68073             return;
68074         }
68075         this.selections.clear();
68076         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
68077             this.selectRow(i, true);
68078         }
68079     },
68080
68081     /**
68082      * Returns <tt>true</tt> if there is a selection.
68083      * @return {Boolean}
68084      */
68085     hasSelection : function(){
68086         return this.selections.length > 0;
68087     },
68088
68089     /**
68090      * Returns <tt>true</tt> if the specified row is selected.
68091      * @param {Number/Record} index The record or index of the record to check
68092      * @return {Boolean}
68093      */
68094     isSelected : function(index){
68095         var r = Ext.isNumber(index) ? this.grid.store.getAt(index) : index;
68096         return (r && this.selections.key(r.id) ? true : false);
68097     },
68098
68099     /**
68100      * Returns <tt>true</tt> if the specified record id is selected.
68101      * @param {String} id The id of record to check
68102      * @return {Boolean}
68103      */
68104     isIdSelected : function(id){
68105         return (this.selections.key(id) ? true : false);
68106     },
68107
68108     // private
68109     handleMouseDown : function(g, rowIndex, e){
68110         if(e.button !== 0 || this.isLocked()){
68111             return;
68112         }
68113         var view = this.grid.getView();
68114         if(e.shiftKey && !this.singleSelect && this.last !== false){
68115             var last = this.last;
68116             this.selectRange(last, rowIndex, e.ctrlKey);
68117             this.last = last; // reset the last
68118             view.focusRow(rowIndex);
68119         }else{
68120             var isSelected = this.isSelected(rowIndex);
68121             if(e.ctrlKey && isSelected){
68122                 this.deselectRow(rowIndex);
68123             }else if(!isSelected || this.getCount() > 1){
68124                 this.selectRow(rowIndex, e.ctrlKey || e.shiftKey);
68125                 view.focusRow(rowIndex);
68126             }
68127         }
68128     },
68129
68130     /**
68131      * Selects multiple rows.
68132      * @param {Array} rows Array of the indexes of the row to select
68133      * @param {Boolean} keepExisting (optional) <tt>true</tt> to keep
68134      * existing selections (defaults to <tt>false</tt>)
68135      */
68136     selectRows : function(rows, keepExisting){
68137         if(!keepExisting){
68138             this.clearSelections();
68139         }
68140         for(var i = 0, len = rows.length; i < len; i++){
68141             this.selectRow(rows[i], true);
68142         }
68143     },
68144
68145     /**
68146      * Selects a range of rows if the selection model
68147      * {@link Ext.grid.AbstractSelectionModel#isLocked is not locked}.
68148      * All rows in between startRow and endRow are also selected.
68149      * @param {Number} startRow The index of the first row in the range
68150      * @param {Number} endRow The index of the last row in the range
68151      * @param {Boolean} keepExisting (optional) True to retain existing selections
68152      */
68153     selectRange : function(startRow, endRow, keepExisting){
68154         var i;
68155         if(this.isLocked()){
68156             return;
68157         }
68158         if(!keepExisting){
68159             this.clearSelections();
68160         }
68161         if(startRow <= endRow){
68162             for(i = startRow; i <= endRow; i++){
68163                 this.selectRow(i, true);
68164             }
68165         }else{
68166             for(i = startRow; i >= endRow; i--){
68167                 this.selectRow(i, true);
68168             }
68169         }
68170     },
68171
68172     /**
68173      * Deselects a range of rows if the selection model
68174      * {@link Ext.grid.AbstractSelectionModel#isLocked is not locked}.  
68175      * All rows in between startRow and endRow are also deselected.
68176      * @param {Number} startRow The index of the first row in the range
68177      * @param {Number} endRow The index of the last row in the range
68178      */
68179     deselectRange : function(startRow, endRow, preventViewNotify){
68180         if(this.isLocked()){
68181             return;
68182         }
68183         for(var i = startRow; i <= endRow; i++){
68184             this.deselectRow(i, preventViewNotify);
68185         }
68186     },
68187
68188     /**
68189      * Selects a row.  Before selecting a row, checks if the selection model
68190      * {@link Ext.grid.AbstractSelectionModel#isLocked is locked} and fires the
68191      * {@link #beforerowselect} event.  If these checks are satisfied the row
68192      * will be selected and followed up by  firing the {@link #rowselect} and
68193      * {@link #selectionchange} events.
68194      * @param {Number} row The index of the row to select
68195      * @param {Boolean} keepExisting (optional) <tt>true</tt> to keep existing selections
68196      * @param {Boolean} preventViewNotify (optional) Specify <tt>true</tt> to
68197      * prevent notifying the view (disables updating the selected appearance)
68198      */
68199     selectRow : function(index, keepExisting, preventViewNotify){
68200         if(this.isLocked() || (index < 0 || index >= this.grid.store.getCount()) || (keepExisting && this.isSelected(index))){
68201             return;
68202         }
68203         var r = this.grid.store.getAt(index);
68204         if(r && this.fireEvent('beforerowselect', this, index, keepExisting, r) !== false){
68205             if(!keepExisting || this.singleSelect){
68206                 this.clearSelections();
68207             }
68208             this.selections.add(r);
68209             this.last = this.lastActive = index;
68210             if(!preventViewNotify){
68211                 this.grid.getView().onRowSelect(index);
68212             }
68213             this.fireEvent('rowselect', this, index, r);
68214             this.fireEvent('selectionchange', this);
68215         }
68216     },
68217
68218     /**
68219      * Deselects a row.  Before deselecting a row, checks if the selection model
68220      * {@link Ext.grid.AbstractSelectionModel#isLocked is locked}.
68221      * If this check is satisfied the row will be deselected and followed up by
68222      * firing the {@link #rowdeselect} and {@link #selectionchange} events.
68223      * @param {Number} row The index of the row to deselect
68224      * @param {Boolean} preventViewNotify (optional) Specify <tt>true</tt> to
68225      * prevent notifying the view (disables updating the selected appearance)
68226      */
68227     deselectRow : function(index, preventViewNotify){
68228         if(this.isLocked()){
68229             return;
68230         }
68231         if(this.last == index){
68232             this.last = false;
68233         }
68234         if(this.lastActive == index){
68235             this.lastActive = false;
68236         }
68237         var r = this.grid.store.getAt(index);
68238         if(r){
68239             this.selections.remove(r);
68240             if(!preventViewNotify){
68241                 this.grid.getView().onRowDeselect(index);
68242             }
68243             this.fireEvent('rowdeselect', this, index, r);
68244             this.fireEvent('selectionchange', this);
68245         }
68246     },
68247
68248     // private
68249     restoreLast : function(){
68250         if(this._last){
68251             this.last = this._last;
68252         }
68253     },
68254
68255     // private
68256     acceptsNav : function(row, col, cm){
68257         return !cm.isHidden(col) && cm.isCellEditable(col, row);
68258     },
68259
68260     // private
68261     onEditorKey : function(field, e){
68262         var k = e.getKey(), 
68263             newCell, 
68264             g = this.grid, 
68265             last = g.lastEdit,
68266             ed = g.activeEditor,
68267             ae, last, r, c;
68268         var shift = e.shiftKey;
68269         if(k == e.TAB){
68270             e.stopEvent();
68271             ed.completeEdit();
68272             if(shift){
68273                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
68274             }else{
68275                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
68276             }
68277         }else if(k == e.ENTER){
68278             if(this.moveEditorOnEnter !== false){
68279                 if(shift){
68280                     newCell = g.walkCells(last.row - 1, last.col, -1, this.acceptsNav, this);
68281                 }else{
68282                     newCell = g.walkCells(last.row + 1, last.col, 1, this.acceptsNav, this);
68283                 }
68284             }
68285         }
68286         if(newCell){
68287             r = newCell[0];
68288             c = newCell[1];
68289
68290             if(last.row != r){
68291                 this.selectRow(r); // *** highlight newly-selected cell and update selection
68292             }
68293
68294             if(g.isEditor && g.editing){ // *** handle tabbing while editorgrid is in edit mode
68295                 ae = g.activeEditor;
68296                 if(ae && ae.field.triggerBlur){
68297                     // *** if activeEditor is a TriggerField, explicitly call its triggerBlur() method
68298                     ae.field.triggerBlur();
68299                 }
68300             }
68301             g.startEditing(r, c);
68302         }
68303     },
68304     
68305     destroy : function(){
68306         if(this.rowNav){
68307             this.rowNav.disable();
68308             this.rowNav = null;
68309         }
68310         Ext.grid.RowSelectionModel.superclass.destroy.call(this);
68311     }
68312 });/**\r
68313  * @class Ext.grid.Column\r
68314  * <p>This class encapsulates column configuration data to be used in the initialization of a\r
68315  * {@link Ext.grid.ColumnModel ColumnModel}.</p>\r
68316  * <p>While subclasses are provided to render data in different ways, this class renders a passed\r
68317  * data field unchanged and is usually used for textual columns.</p>\r
68318  */\r
68319 Ext.grid.Column = Ext.extend(Object, {\r
68320     /**\r
68321      * @cfg {Boolean} editable Optional. Defaults to <tt>true</tt>, enabling the configured\r
68322      * <tt>{@link #editor}</tt>.  Set to <tt>false</tt> to initially disable editing on this column.\r
68323      * The initial configuration may be dynamically altered using\r
68324      * {@link Ext.grid.ColumnModel}.{@link Ext.grid.ColumnModel#setEditable setEditable()}.\r
68325      */\r
68326     /**\r
68327      * @cfg {String} id Optional. A name which identifies this column (defaults to the column's initial\r
68328      * ordinal position.) The <tt>id</tt> is used to create a CSS <b>class</b> name which is applied to all\r
68329      * table cells (including headers) in that column (in this context the <tt>id</tt> does not need to be\r
68330      * unique). The class name takes the form of <pre>x-grid3-td-<b>id</b></pre>\r
68331      * Header cells will also receive this class name, but will also have the class <pre>x-grid3-hd</pre>\r
68332      * So, to target header cells, use CSS selectors such as:<pre>.x-grid3-hd-row .x-grid3-td-<b>id</b></pre>\r
68333      * The {@link Ext.grid.GridPanel#autoExpandColumn} grid config option references the column via this\r
68334      * unique identifier.\r
68335      */\r
68336     /**\r
68337      * @cfg {String} header Optional. The header text to be used as innerHTML\r
68338      * (html tags are accepted) to display in the Grid view.  <b>Note</b>: to\r
68339      * have a clickable header with no text displayed use <tt>'&#160;'</tt>.\r
68340      */\r
68341     /**\r
68342      * @cfg {Boolean} groupable Optional. If the grid is being rendered by an {@link Ext.grid.GroupingView}, this option\r
68343      * may be used to disable the header menu item to group by the column selected. Defaults to <tt>true</tt>,\r
68344      * which enables the header menu group option.  Set to <tt>false</tt> to disable (but still show) the\r
68345      * group option in the header menu for the column. See also <code>{@link #groupName}</code>.\r
68346      */\r
68347     /**\r
68348      * @cfg {String} groupName Optional. If the grid is being rendered by an {@link Ext.grid.GroupingView}, this option\r
68349      * may be used to specify the text with which to prefix the group field value in the group header line.\r
68350      * See also {@link #groupRenderer} and\r
68351      * {@link Ext.grid.GroupingView}.{@link Ext.grid.GroupingView#showGroupName showGroupName}.\r
68352      */\r
68353     /**\r
68354      * @cfg {Function} groupRenderer <p>Optional. If the grid is being rendered by an {@link Ext.grid.GroupingView}, this option\r
68355      * may be used to specify the function used to format the grouping field value for display in the group\r
68356      * {@link #groupName header}.  If a <tt><b>groupRenderer</b></tt> is not specified, the configured\r
68357      * <tt><b>{@link #renderer}</b></tt> will be called; if a <tt><b>{@link #renderer}</b></tt> is also not specified\r
68358      * the new value of the group field will be used.</p>\r
68359      * <p>The called function (either the <tt><b>groupRenderer</b></tt> or <tt><b>{@link #renderer}</b></tt>) will be\r
68360      * passed the following parameters:\r
68361      * <div class="mdetail-params"><ul>\r
68362      * <li><b>v</b> : Object<p class="sub-desc">The new value of the group field.</p></li>\r
68363      * <li><b>unused</b> : undefined<p class="sub-desc">Unused parameter.</p></li>\r
68364      * <li><b>r</b> : Ext.data.Record<p class="sub-desc">The Record providing the data\r
68365      * for the row which caused group change.</p></li>\r
68366      * <li><b>rowIndex</b> : Number<p class="sub-desc">The row index of the Record which caused group change.</p></li>\r
68367      * <li><b>colIndex</b> : Number<p class="sub-desc">The column index of the group field.</p></li>\r
68368      * <li><b>ds</b> : Ext.data.Store<p class="sub-desc">The Store which is providing the data Model.</p></li>\r
68369      * </ul></div></p>\r
68370      * <p>The function should return a string value.</p>\r
68371      */\r
68372     /**\r
68373      * @cfg {String} emptyGroupText Optional. If the grid is being rendered by an {@link Ext.grid.GroupingView}, this option\r
68374      * may be used to specify the text to display when there is an empty group value. Defaults to the\r
68375      * {@link Ext.grid.GroupingView}.{@link Ext.grid.GroupingView#emptyGroupText emptyGroupText}.\r
68376      */\r
68377     /**\r
68378      * @cfg {String} dataIndex <p><b>Required</b>. The name of the field in the\r
68379      * grid's {@link Ext.data.Store}'s {@link Ext.data.Record} definition from\r
68380      * which to draw the column's value.</p>\r
68381      */\r
68382     /**\r
68383      * @cfg {Number} width\r
68384      * Optional. The initial width in pixels of the column.\r
68385      * The width of each column can also be affected if any of the following are configured:\r
68386      * <div class="mdetail-params"><ul>\r
68387      * <li>{@link Ext.grid.GridPanel}.<tt>{@link Ext.grid.GridPanel#autoExpandColumn autoExpandColumn}</tt></li>\r
68388      * <li>{@link Ext.grid.GridView}.<tt>{@link Ext.grid.GridView#forceFit forceFit}</tt>\r
68389      * <div class="sub-desc">\r
68390      * <p>By specifying <tt>forceFit:true</tt>, {@link #fixed non-fixed width} columns will be\r
68391      * re-proportioned (based on the relative initial widths) to fill the width of the grid so\r
68392      * that no horizontal scrollbar is shown.</p>\r
68393      * </div></li>\r
68394      * <li>{@link Ext.grid.GridView}.<tt>{@link Ext.grid.GridView#autoFill autoFill}</tt></li>\r
68395      * <li>{@link Ext.grid.GridPanel}.<tt>{@link Ext.grid.GridPanel#minColumnWidth minColumnWidth}</tt></li>\r
68396      * <br><p><b>Note</b>: when the width of each column is determined, a space on the right side\r
68397      * is reserved for the vertical scrollbar.  The\r
68398      * {@link Ext.grid.GridView}.<tt>{@link Ext.grid.GridView#scrollOffset scrollOffset}</tt>\r
68399      * can be modified to reduce or eliminate the reserved offset.</p>\r
68400      */\r
68401     /**\r
68402      * @cfg {Boolean} sortable Optional. <tt>true</tt> if sorting is to be allowed on this column.\r
68403      * Defaults to the value of the <code>{@link Ext.grid.ColumnModel#defaultSortable}</code> property.\r
68404      * Whether local/remote sorting is used is specified in <code>{@link Ext.data.Store#remoteSort}</code>.\r
68405      */\r
68406     /**\r
68407      * @cfg {Boolean} fixed Optional. <tt>true</tt> if the column width cannot be changed.  Defaults to <tt>false</tt>.\r
68408      */\r
68409     /**\r
68410      * @cfg {Boolean} resizable Optional. <tt>false</tt> to disable column resizing. Defaults to <tt>true</tt>.\r
68411      */\r
68412     /**\r
68413      * @cfg {Boolean} menuDisabled Optional. <tt>true</tt> to disable the column menu. Defaults to <tt>false</tt>.\r
68414      */\r
68415     /**\r
68416      * @cfg {Boolean} hidden\r
68417      * Optional. <tt>true</tt> to initially hide this column. Defaults to <tt>false</tt>.\r
68418      * A hidden column {@link Ext.grid.GridPanel#enableColumnHide may be shown via the header row menu}.\r
68419      * If a column is never to be shown, simply do not include this column in the Column Model at all. \r
68420      */\r
68421     /**\r
68422      * @cfg {String} tooltip Optional. A text string to use as the column header's tooltip.  If Quicktips\r
68423      * are enabled, this value will be used as the text of the quick tip, otherwise it will be set as the\r
68424      * header's HTML title attribute. Defaults to ''.\r
68425      */\r
68426     /**\r
68427      * @cfg {Mixed} renderer\r
68428      * <p>For an alternative to specifying a renderer see <code>{@link #xtype}</code></p>\r
68429      * <p>Optional. A renderer is an 'interceptor' method which can be used transform data (value,\r
68430      * appearance, etc.) before it is rendered). This may be specified in either of three ways:\r
68431      * <div class="mdetail-params"><ul>\r
68432      * <li>A renderer function used to return HTML markup for a cell given the cell's data value.</li>\r
68433      * <li>A string which references a property name of the {@link Ext.util.Format} class which\r
68434      * provides a renderer function.</li>\r
68435      * <li>An object specifying both the renderer function, and its execution scope (<tt><b>this</b></tt>\r
68436      * reference) e.g.:<pre style="margin-left:1.2em"><code>\r
68437 {\r
68438     fn: this.gridRenderer,\r
68439     scope: this\r
68440 }\r
68441 </code></pre></li></ul></div>\r
68442      * If not specified, the default renderer uses the raw data value.</p>\r
68443      * <p>For information about the renderer function (passed parameters, etc.), see\r
68444      * {@link Ext.grid.ColumnModel#setRenderer}. An example of specifying renderer function inline:</p><pre><code>\r
68445 var companyColumn = {\r
68446    header: 'Company Name',\r
68447    dataIndex: 'company',\r
68448    renderer: function(value, metaData, record, rowIndex, colIndex, store) {\r
68449       // provide the logic depending on business rules\r
68450       // name of your own choosing to manipulate the cell depending upon\r
68451       // the data in the underlying Record object.\r
68452       if (value == 'whatever') {\r
68453           //metaData.css : String : A CSS class name to add to the TD element of the cell.\r
68454           //metaData.attr : String : An html attribute definition string to apply to\r
68455           //                         the data container element within the table\r
68456           //                         cell (e.g. 'style="color:red;"').\r
68457           metaData.css = 'name-of-css-class-you-will-define';\r
68458       }\r
68459       return value;\r
68460    }\r
68461 }\r
68462      * </code></pre>\r
68463      * See also {@link #scope}.\r
68464      */\r
68465     /**\r
68466      * @cfg {String} xtype Optional. A String which references a predefined {@link Ext.grid.Column} subclass\r
68467      * type which is preconfigured with an appropriate <code>{@link #renderer}</code> to be easily\r
68468      * configured into a ColumnModel. The predefined {@link Ext.grid.Column} subclass types are:\r
68469      * <div class="mdetail-params"><ul>\r
68470      * <li><b><tt>gridcolumn</tt></b> : {@link Ext.grid.Column} (<b>Default</b>)<p class="sub-desc"></p></li>\r
68471      * <li><b><tt>booleancolumn</tt></b> : {@link Ext.grid.BooleanColumn}<p class="sub-desc"></p></li>\r
68472      * <li><b><tt>numbercolumn</tt></b> : {@link Ext.grid.NumberColumn}<p class="sub-desc"></p></li>\r
68473      * <li><b><tt>datecolumn</tt></b> : {@link Ext.grid.DateColumn}<p class="sub-desc"></p></li>\r
68474      * <li><b><tt>templatecolumn</tt></b> : {@link Ext.grid.TemplateColumn}<p class="sub-desc"></p></li>\r
68475      * </ul></div>\r
68476      * <p>Configuration properties for the specified <code>xtype</code> may be specified with\r
68477      * the Column configuration properties, for example:</p>\r
68478      * <pre><code>\r
68479 var grid = new Ext.grid.GridPanel({\r
68480     ...\r
68481     columns: [{\r
68482         header: 'Last Updated',\r
68483         dataIndex: 'lastChange',\r
68484         width: 85,\r
68485         sortable: true,\r
68486         //renderer: Ext.util.Format.dateRenderer('m/d/Y'),\r
68487         xtype: 'datecolumn', // use xtype instead of renderer\r
68488         format: 'M/d/Y' // configuration property for {@link Ext.grid.DateColumn}\r
68489     }, {\r
68490         ...\r
68491     }]\r
68492 });\r
68493      * </code></pre>\r
68494      */\r
68495     /**\r
68496      * @cfg {Object} scope Optional. The scope (<tt><b>this</b></tt> reference) in which to execute the\r
68497      * renderer.  Defaults to the Column configuration object.\r
68498      */\r
68499     /**\r
68500      * @cfg {String} align Optional. Set the CSS text-align property of the column.  Defaults to undefined.\r
68501      */\r
68502     /**\r
68503      * @cfg {String} css Optional. An inline style definition string which is applied to all table cells in the column\r
68504      * (excluding headers). Defaults to undefined.\r
68505      */\r
68506     /**\r
68507      * @cfg {Boolean} hideable Optional. Specify as <tt>false</tt> to prevent the user from hiding this column\r
68508      * (defaults to true).  To disallow column hiding globally for all columns in the grid, use\r
68509      * {@link Ext.grid.GridPanel#enableColumnHide} instead.\r
68510      */\r
68511     /**\r
68512      * @cfg {Ext.form.Field} editor Optional. The {@link Ext.form.Field} to use when editing values in this column\r
68513      * if editing is supported by the grid. See <tt>{@link #editable}</tt> also.\r
68514      */\r
68515 \r
68516     /**\r
68517      * @private\r
68518      * @cfg {Boolean} isColumn\r
68519      * Used by ColumnModel setConfig method to avoid reprocessing a Column\r
68520      * if <code>isColumn</code> is not set ColumnModel will recreate a new Ext.grid.Column\r
68521      * Defaults to true.\r
68522      */\r
68523     isColumn : true,\r
68524     \r
68525     constructor : function(config){\r
68526         Ext.apply(this, config);\r
68527         \r
68528         if(Ext.isString(this.renderer)){\r
68529             this.renderer = Ext.util.Format[this.renderer];\r
68530         }else if(Ext.isObject(this.renderer)){\r
68531             this.scope = this.renderer.scope;\r
68532             this.renderer = this.renderer.fn;\r
68533         }\r
68534         if(!this.scope){\r
68535             this.scope = this;\r
68536         }\r
68537         \r
68538         var ed = this.editor;\r
68539         delete this.editor;\r
68540         this.setEditor(ed);\r
68541     },\r
68542 \r
68543     /**\r
68544      * Optional. A function which returns displayable data when passed the following parameters:\r
68545      * <div class="mdetail-params"><ul>\r
68546      * <li><b>value</b> : Object<p class="sub-desc">The data value for the cell.</p></li>\r
68547      * <li><b>metadata</b> : Object<p class="sub-desc">An object in which you may set the following attributes:<ul>\r
68548      * <li><b>css</b> : String<p class="sub-desc">A CSS class name to add to the cell's TD element.</p></li>\r
68549      * <li><b>attr</b> : String<p class="sub-desc">An HTML attribute definition string to apply to the data container\r
68550      * element <i>within</i> the table cell (e.g. 'style="color:red;"').</p></li></ul></p></li>\r
68551      * <li><b>record</b> : Ext.data.record<p class="sub-desc">The {@link Ext.data.Record} from which the data was\r
68552      * extracted.</p></li>\r
68553      * <li><b>rowIndex</b> : Number<p class="sub-desc">Row index</p></li>\r
68554      * <li><b>colIndex</b> : Number<p class="sub-desc">Column index</p></li>\r
68555      * <li><b>store</b> : Ext.data.Store<p class="sub-desc">The {@link Ext.data.Store} object from which the Record\r
68556      * was extracted.</p></li>\r
68557      * </ul></div>\r
68558      * @property renderer\r
68559      * @type Function\r
68560      */\r
68561     renderer : function(value){\r
68562         if(Ext.isString(value) && value.length < 1){\r
68563             return '&#160;';\r
68564         }\r
68565         return value;\r
68566     },\r
68567 \r
68568     // private\r
68569     getEditor: function(rowIndex){\r
68570         return this.editable !== false ? this.editor : null;\r
68571     },\r
68572     \r
68573     /**\r
68574      * Sets a new editor for this column.\r
68575      * @param {Ext.Editor/Ext.form.Field} editor The editor to set\r
68576      */\r
68577     setEditor : function(editor){\r
68578         if(this.editor){\r
68579             this.editor.destroy();\r
68580         }\r
68581         this.editor = null;\r
68582         if(editor){\r
68583             //not an instance, create it\r
68584             if(!editor.isXType){\r
68585                 editor = Ext.create(editor, 'textfield');\r
68586             }\r
68587             //check if it's wrapped in an editor\r
68588             if(!editor.startEdit){\r
68589                 editor = new Ext.grid.GridEditor(editor);\r
68590             }\r
68591             this.editor = editor;\r
68592         }\r
68593     },\r
68594     \r
68595     destroy : function(){\r
68596         this.setEditor(null);\r
68597     },\r
68598 \r
68599     /**\r
68600      * Returns the {@link Ext.Editor editor} defined for this column that was created to wrap the {@link Ext.form.Field Field}\r
68601      * used to edit the cell.\r
68602      * @param {Number} rowIndex The row index\r
68603      * @return {Ext.Editor}\r
68604      */\r
68605     getCellEditor: function(rowIndex){\r
68606         return this.getEditor(rowIndex);\r
68607     }\r
68608 });\r
68609 \r
68610 /**\r
68611  * @class Ext.grid.BooleanColumn\r
68612  * @extends Ext.grid.Column\r
68613  * <p>A Column definition class which renders boolean data fields.  See the {@link Ext.grid.Column#xtype xtype}\r
68614  * config option of {@link Ext.grid.Column} for more details.</p>\r
68615  */\r
68616 Ext.grid.BooleanColumn = Ext.extend(Ext.grid.Column, {\r
68617     /**\r
68618      * @cfg {String} trueText\r
68619      * The string returned by the renderer when the column value is not falsey (defaults to <tt>'true'</tt>).\r
68620      */\r
68621     trueText: 'true',\r
68622     /**\r
68623      * @cfg {String} falseText\r
68624      * The string returned by the renderer when the column value is falsey (but not undefined) (defaults to\r
68625      * <tt>'false'</tt>).\r
68626      */\r
68627     falseText: 'false',\r
68628     /**\r
68629      * @cfg {String} undefinedText\r
68630      * The string returned by the renderer when the column value is undefined (defaults to <tt>'&#160;'</tt>).\r
68631      */\r
68632     undefinedText: '&#160;',\r
68633 \r
68634     constructor: function(cfg){\r
68635         Ext.grid.BooleanColumn.superclass.constructor.call(this, cfg);\r
68636         var t = this.trueText, f = this.falseText, u = this.undefinedText;\r
68637         this.renderer = function(v){\r
68638             if(v === undefined){\r
68639                 return u;\r
68640             }\r
68641             if(!v || v === 'false'){\r
68642                 return f;\r
68643             }\r
68644             return t;\r
68645         };\r
68646     }\r
68647 });\r
68648 \r
68649 /**\r
68650  * @class Ext.grid.NumberColumn\r
68651  * @extends Ext.grid.Column\r
68652  * <p>A Column definition class which renders a numeric data field according to a {@link #format} string.  See the\r
68653  * {@link Ext.grid.Column#xtype xtype} config option of {@link Ext.grid.Column} for more details.</p>\r
68654  */\r
68655 Ext.grid.NumberColumn = Ext.extend(Ext.grid.Column, {\r
68656     /**\r
68657      * @cfg {String} format\r
68658      * A formatting string as used by {@link Ext.util.Format#number} to format a numeric value for this Column\r
68659      * (defaults to <tt>'0,000.00'</tt>).\r
68660      */\r
68661     format : '0,000.00',\r
68662     constructor: function(cfg){\r
68663         Ext.grid.NumberColumn.superclass.constructor.call(this, cfg);\r
68664         this.renderer = Ext.util.Format.numberRenderer(this.format);\r
68665     }\r
68666 });\r
68667 \r
68668 /**\r
68669  * @class Ext.grid.DateColumn\r
68670  * @extends Ext.grid.Column\r
68671  * <p>A Column definition class which renders a passed date according to the default locale, or a configured\r
68672  * {@link #format}. See the {@link Ext.grid.Column#xtype xtype} config option of {@link Ext.grid.Column}\r
68673  * for more details.</p>\r
68674  */\r
68675 Ext.grid.DateColumn = Ext.extend(Ext.grid.Column, {\r
68676     /**\r
68677      * @cfg {String} format\r
68678      * A formatting string as used by {@link Date#format} to format a Date for this Column\r
68679      * (defaults to <tt>'m/d/Y'</tt>).\r
68680      */\r
68681     format : 'm/d/Y',\r
68682     constructor: function(cfg){\r
68683         Ext.grid.DateColumn.superclass.constructor.call(this, cfg);\r
68684         this.renderer = Ext.util.Format.dateRenderer(this.format);\r
68685     }\r
68686 });\r
68687 \r
68688 /**\r
68689  * @class Ext.grid.TemplateColumn\r
68690  * @extends Ext.grid.Column\r
68691  * <p>A Column definition class which renders a value by processing a {@link Ext.data.Record Record}'s\r
68692  * {@link Ext.data.Record#data data} using a {@link #tpl configured} {@link Ext.XTemplate XTemplate}.\r
68693  * See the {@link Ext.grid.Column#xtype xtype} config option of {@link Ext.grid.Column} for more\r
68694  * details.</p>\r
68695  */\r
68696 Ext.grid.TemplateColumn = Ext.extend(Ext.grid.Column, {\r
68697     /**\r
68698      * @cfg {String/XTemplate} tpl\r
68699      * An {@link Ext.XTemplate XTemplate}, or an XTemplate <i>definition string</i> to use to process a\r
68700      * {@link Ext.data.Record Record}'s {@link Ext.data.Record#data data} to produce a column's rendered value.\r
68701      */\r
68702     constructor: function(cfg){\r
68703         Ext.grid.TemplateColumn.superclass.constructor.call(this, cfg);\r
68704         var tpl = (!Ext.isPrimitive(this.tpl) && this.tpl.compile) ? this.tpl : new Ext.XTemplate(this.tpl);\r
68705         this.renderer = function(value, p, r){\r
68706             return tpl.apply(r.data);\r
68707         };\r
68708         this.tpl = tpl;\r
68709     }\r
68710 });\r
68711 \r
68712 /*\r
68713  * @property types\r
68714  * @type Object\r
68715  * @member Ext.grid.Column\r
68716  * @static\r
68717  * <p>An object containing predefined Column classes keyed by a mnemonic code which may be referenced\r
68718  * by the {@link Ext.grid.ColumnModel#xtype xtype} config option of ColumnModel.</p>\r
68719  * <p>This contains the following properties</p><div class="mdesc-details"><ul>\r
68720  * <li>gridcolumn : <b>{@link Ext.grid.Column Column constructor}</b></li>\r
68721  * <li>booleancolumn : <b>{@link Ext.grid.BooleanColumn BooleanColumn constructor}</b></li>\r
68722  * <li>numbercolumn : <b>{@link Ext.grid.NumberColumn NumberColumn constructor}</b></li>\r
68723  * <li>datecolumn : <b>{@link Ext.grid.DateColumn DateColumn constructor}</b></li>\r
68724  * <li>templatecolumn : <b>{@link Ext.grid.TemplateColumn TemplateColumn constructor}</b></li>\r
68725  * </ul></div>\r
68726  */\r
68727 Ext.grid.Column.types = {\r
68728     gridcolumn : Ext.grid.Column,\r
68729     booleancolumn: Ext.grid.BooleanColumn,\r
68730     numbercolumn: Ext.grid.NumberColumn,\r
68731     datecolumn: Ext.grid.DateColumn,\r
68732     templatecolumn: Ext.grid.TemplateColumn\r
68733 };/**
68734  * @class Ext.grid.RowNumberer
68735  * This is a utility class that can be passed into a {@link Ext.grid.ColumnModel} as a column config that provides
68736  * an automatic row numbering column.
68737  * <br>Usage:<br>
68738  <pre><code>
68739  // This is a typical column config with the first column providing row numbers
68740  var colModel = new Ext.grid.ColumnModel([
68741     new Ext.grid.RowNumberer(),
68742     {header: "Name", width: 80, sortable: true},
68743     {header: "Code", width: 50, sortable: true},
68744     {header: "Description", width: 200, sortable: true}
68745  ]);
68746  </code></pre>
68747  * @constructor
68748  * @param {Object} config The configuration options
68749  */
68750 Ext.grid.RowNumberer = Ext.extend(Object, {
68751     /**
68752      * @cfg {String} header Any valid text or HTML fragment to display in the header cell for the row
68753      * number column (defaults to '').
68754      */
68755     header: "",
68756     /**
68757      * @cfg {Number} width The default width in pixels of the row number column (defaults to 23).
68758      */
68759     width: 23,
68760     /**
68761      * @cfg {Boolean} sortable True if the row number column is sortable (defaults to false).
68762      * @hide
68763      */
68764     sortable: false,
68765     
68766     constructor : function(config){
68767         Ext.apply(this, config);
68768         if(this.rowspan){
68769             this.renderer = this.renderer.createDelegate(this);
68770         }
68771     },
68772
68773     // private
68774     fixed:true,
68775     menuDisabled:true,
68776     dataIndex: '',
68777     id: 'numberer',
68778     rowspan: undefined,
68779
68780     // private
68781     renderer : function(v, p, record, rowIndex){
68782         if(this.rowspan){
68783             p.cellAttr = 'rowspan="'+this.rowspan+'"';
68784         }
68785         return rowIndex+1;
68786     }
68787 });/**\r
68788  * @class Ext.grid.CheckboxSelectionModel\r
68789  * @extends Ext.grid.RowSelectionModel\r
68790  * A custom selection model that renders a column of checkboxes that can be toggled to select or deselect rows.\r
68791  * @constructor\r
68792  * @param {Object} config The configuration options\r
68793  */\r
68794 Ext.grid.CheckboxSelectionModel = Ext.extend(Ext.grid.RowSelectionModel, {\r
68795 \r
68796     /**\r
68797      * @cfg {Boolean} checkOnly <tt>true</tt> if rows can only be selected by clicking on the\r
68798      * checkbox column (defaults to <tt>false</tt>).\r
68799      */\r
68800     /**\r
68801      * @cfg {String} header Any valid text or HTML fragment to display in the header cell for the\r
68802      * checkbox column.  Defaults to:<pre><code>\r
68803      * '&lt;div class="x-grid3-hd-checker">&#38;#160;&lt;/div>'</tt>\r
68804      * </code></pre>\r
68805      * The default CSS class of <tt>'x-grid3-hd-checker'</tt> displays a checkbox in the header\r
68806      * and provides support for automatic check all/none behavior on header click. This string\r
68807      * can be replaced by any valid HTML fragment, including a simple text string (e.g.,\r
68808      * <tt>'Select Rows'</tt>), but the automatic check all/none behavior will only work if the\r
68809      * <tt>'x-grid3-hd-checker'</tt> class is supplied.\r
68810      */\r
68811     header : '<div class="x-grid3-hd-checker">&#160;</div>',\r
68812     /**\r
68813      * @cfg {Number} width The default width in pixels of the checkbox column (defaults to <tt>20</tt>).\r
68814      */\r
68815     width : 20,\r
68816     /**\r
68817      * @cfg {Boolean} sortable <tt>true</tt> if the checkbox column is sortable (defaults to\r
68818      * <tt>false</tt>).\r
68819      */\r
68820     sortable : false,\r
68821 \r
68822     // private\r
68823     menuDisabled : true,\r
68824     fixed : true,\r
68825     dataIndex : '',\r
68826     id : 'checker',\r
68827 \r
68828     constructor : function(){\r
68829         Ext.grid.CheckboxSelectionModel.superclass.constructor.apply(this, arguments);\r
68830 \r
68831         if(this.checkOnly){\r
68832             this.handleMouseDown = Ext.emptyFn;\r
68833         }\r
68834     },\r
68835 \r
68836     // private\r
68837     initEvents : function(){\r
68838         Ext.grid.CheckboxSelectionModel.superclass.initEvents.call(this);\r
68839         this.grid.on('render', function(){\r
68840             var view = this.grid.getView();\r
68841             view.mainBody.on('mousedown', this.onMouseDown, this);\r
68842             Ext.fly(view.innerHd).on('mousedown', this.onHdMouseDown, this);\r
68843 \r
68844         }, this);\r
68845     },\r
68846 \r
68847     // private\r
68848     onMouseDown : function(e, t){\r
68849         if(e.button === 0 && t.className == 'x-grid3-row-checker'){ // Only fire if left-click\r
68850             e.stopEvent();\r
68851             var row = e.getTarget('.x-grid3-row');\r
68852             if(row){\r
68853                 var index = row.rowIndex;\r
68854                 if(this.isSelected(index)){\r
68855                     this.deselectRow(index);\r
68856                 }else{\r
68857                     this.selectRow(index, true);\r
68858                 }\r
68859             }\r
68860         }\r
68861     },\r
68862 \r
68863     // private\r
68864     onHdMouseDown : function(e, t){\r
68865         if(t.className == 'x-grid3-hd-checker'){\r
68866             e.stopEvent();\r
68867             var hd = Ext.fly(t.parentNode);\r
68868             var isChecked = hd.hasClass('x-grid3-hd-checker-on');\r
68869             if(isChecked){\r
68870                 hd.removeClass('x-grid3-hd-checker-on');\r
68871                 this.clearSelections();\r
68872             }else{\r
68873                 hd.addClass('x-grid3-hd-checker-on');\r
68874                 this.selectAll();\r
68875             }\r
68876         }\r
68877     },\r
68878 \r
68879     // private\r
68880     renderer : function(v, p, record){\r
68881         return '<div class="x-grid3-row-checker">&#160;</div>';\r
68882     }\r
68883 });/**
68884  * @class Ext.grid.CellSelectionModel
68885  * @extends Ext.grid.AbstractSelectionModel
68886  * This class provides the basic implementation for <i>single</i> <b>cell</b> selection in a grid.
68887  * The object stored as the selection contains the following properties:
68888  * <div class="mdetail-params"><ul>
68889  * <li><b>cell</b> : see {@link #getSelectedCell} 
68890  * <li><b>record</b> : Ext.data.record The {@link Ext.data.Record Record}
68891  * which provides the data for the row containing the selection</li>
68892  * </ul></div>
68893  * @constructor
68894  * @param {Object} config The object containing the configuration of this model.
68895  */
68896 Ext.grid.CellSelectionModel = Ext.extend(Ext.grid.AbstractSelectionModel,  {
68897     
68898     constructor : function(config){
68899         Ext.apply(this, config);
68900
68901             this.selection = null;
68902         
68903             this.addEvents(
68904                 /**
68905                  * @event beforecellselect
68906                  * Fires before a cell is selected, return false to cancel the selection.
68907                  * @param {SelectionModel} this
68908                  * @param {Number} rowIndex The selected row index
68909                  * @param {Number} colIndex The selected cell index
68910                  */
68911                 "beforecellselect",
68912                 /**
68913                  * @event cellselect
68914                  * Fires when a cell is selected.
68915                  * @param {SelectionModel} this
68916                  * @param {Number} rowIndex The selected row index
68917                  * @param {Number} colIndex The selected cell index
68918                  */
68919                 "cellselect",
68920                 /**
68921                  * @event selectionchange
68922                  * Fires when the active selection changes.
68923                  * @param {SelectionModel} this
68924                  * @param {Object} selection null for no selection or an object with two properties
68925                  * <div class="mdetail-params"><ul>
68926                  * <li><b>cell</b> : see {@link #getSelectedCell} 
68927                  * <li><b>record</b> : Ext.data.record<p class="sub-desc">The {@link Ext.data.Record Record}
68928                  * which provides the data for the row containing the selection</p></li>
68929                  * </ul></div>
68930                  */
68931                 "selectionchange"
68932             );
68933         
68934             Ext.grid.CellSelectionModel.superclass.constructor.call(this);
68935     },
68936
68937     /** @ignore */
68938     initEvents : function(){
68939         this.grid.on('cellmousedown', this.handleMouseDown, this);
68940         this.grid.on(Ext.EventManager.useKeydown ? 'keydown' : 'keypress', this.handleKeyDown, this);
68941         this.grid.getView().on({
68942             scope: this,
68943             refresh: this.onViewChange,
68944             rowupdated: this.onRowUpdated,
68945             beforerowremoved: this.clearSelections,
68946             beforerowsinserted: this.clearSelections
68947         });
68948         if(this.grid.isEditor){
68949             this.grid.on('beforeedit', this.beforeEdit,  this);
68950         }
68951     },
68952
68953         //private
68954     beforeEdit : function(e){
68955         this.select(e.row, e.column, false, true, e.record);
68956     },
68957
68958         //private
68959     onRowUpdated : function(v, index, r){
68960         if(this.selection && this.selection.record == r){
68961             v.onCellSelect(index, this.selection.cell[1]);
68962         }
68963     },
68964
68965         //private
68966     onViewChange : function(){
68967         this.clearSelections(true);
68968     },
68969
68970         /**
68971      * Returns an array containing the row and column indexes of the currently selected cell
68972      * (e.g., [0, 0]), or null if none selected. The array has elements:
68973      * <div class="mdetail-params"><ul>
68974      * <li><b>rowIndex</b> : Number<p class="sub-desc">The index of the selected row</p></li>
68975      * <li><b>cellIndex</b> : Number<p class="sub-desc">The index of the selected cell. 
68976      * Due to possible column reordering, the cellIndex should <b>not</b> be used as an
68977      * index into the Record's data. Instead, use the cellIndex to determine the <i>name</i>
68978      * of the selected cell and use the field name to retrieve the data value from the record:<pre><code>
68979 // get name
68980 var fieldName = grid.getColumnModel().getDataIndex(cellIndex);
68981 // get data value based on name
68982 var data = record.get(fieldName);
68983      * </code></pre></p></li>
68984      * </ul></div>
68985      * @return {Array} An array containing the row and column indexes of the selected cell, or null if none selected.
68986          */
68987     getSelectedCell : function(){
68988         return this.selection ? this.selection.cell : null;
68989     },
68990
68991     /**
68992      * If anything is selected, clears all selections and fires the selectionchange event.
68993      * @param {Boolean} preventNotify <tt>true</tt> to prevent the gridview from
68994      * being notified about the change.
68995      */
68996     clearSelections : function(preventNotify){
68997         var s = this.selection;
68998         if(s){
68999             if(preventNotify !== true){
69000                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
69001             }
69002             this.selection = null;
69003             this.fireEvent("selectionchange", this, null);
69004         }
69005     },
69006
69007     /**
69008      * Returns <tt>true</tt> if there is a selection.
69009      * @return {Boolean}
69010      */
69011     hasSelection : function(){
69012         return this.selection ? true : false;
69013     },
69014
69015     /** @ignore */
69016     handleMouseDown : function(g, row, cell, e){
69017         if(e.button !== 0 || this.isLocked()){
69018             return;
69019         }
69020         this.select(row, cell);
69021     },
69022
69023     /**
69024      * Selects a cell.  Before selecting a cell, fires the
69025      * {@link #beforecellselect} event.  If this check is satisfied the cell
69026      * will be selected and followed up by  firing the {@link #cellselect} and
69027      * {@link #selectionchange} events.
69028      * @param {Number} rowIndex The index of the row to select
69029      * @param {Number} colIndex The index of the column to select
69030      * @param {Boolean} preventViewNotify (optional) Specify <tt>true</tt> to
69031      * prevent notifying the view (disables updating the selected appearance)
69032      * @param {Boolean} preventFocus (optional) Whether to prevent the cell at
69033      * the specified rowIndex / colIndex from being focused.
69034      * @param {Ext.data.Record} r (optional) The record to select
69035      */
69036     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
69037         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
69038             this.clearSelections();
69039             r = r || this.grid.store.getAt(rowIndex);
69040             this.selection = {
69041                 record : r,
69042                 cell : [rowIndex, colIndex]
69043             };
69044             if(!preventViewNotify){
69045                 var v = this.grid.getView();
69046                 v.onCellSelect(rowIndex, colIndex);
69047                 if(preventFocus !== true){
69048                     v.focusCell(rowIndex, colIndex);
69049                 }
69050             }
69051             this.fireEvent("cellselect", this, rowIndex, colIndex);
69052             this.fireEvent("selectionchange", this, this.selection);
69053         }
69054     },
69055
69056         //private
69057     isSelectable : function(rowIndex, colIndex, cm){
69058         return !cm.isHidden(colIndex);
69059     },
69060     
69061     // private
69062     onEditorKey: function(field, e){
69063         if(e.getKey() == e.TAB){
69064             this.handleKeyDown(e);
69065         }
69066     },
69067
69068     /** @ignore */
69069     handleKeyDown : function(e){
69070         if(!e.isNavKeyPress()){
69071             return;
69072         }
69073         
69074         var k = e.getKey(),
69075             g = this.grid,
69076             s = this.selection,
69077             sm = this,
69078             walk = function(row, col, step){
69079                 return g.walkCells(
69080                     row,
69081                     col,
69082                     step,
69083                     g.isEditor && g.editing ? sm.acceptsNav : sm.isSelectable, // *** handle tabbing while editorgrid is in edit mode
69084                     sm
69085                 );
69086             },
69087             cell, newCell, r, c, ae;
69088
69089         switch(k){
69090             case e.ESC:
69091             case e.PAGE_UP:
69092             case e.PAGE_DOWN:
69093                 // do nothing
69094                 break;
69095             default:
69096                 // *** call e.stopEvent() only for non ESC, PAGE UP/DOWN KEYS
69097                 e.stopEvent();
69098                 break;
69099         }
69100
69101         if(!s){
69102             cell = walk(0, 0, 1); // *** use private walk() function defined above
69103             if(cell){
69104                 this.select(cell[0], cell[1]);
69105             }
69106             return;
69107         }
69108
69109         cell = s.cell;  // currently selected cell
69110         r = cell[0];    // current row
69111         c = cell[1];    // current column
69112         
69113         switch(k){
69114             case e.TAB:
69115                 if(e.shiftKey){
69116                     newCell = walk(r, c - 1, -1);
69117                 }else{
69118                     newCell = walk(r, c + 1, 1);
69119                 }
69120                 break;
69121             case e.DOWN:
69122                 newCell = walk(r + 1, c, 1);
69123                 break;
69124             case e.UP:
69125                 newCell = walk(r - 1, c, -1);
69126                 break;
69127             case e.RIGHT:
69128                 newCell = walk(r, c + 1, 1);
69129                 break;
69130             case e.LEFT:
69131                 newCell = walk(r, c - 1, -1);
69132                 break;
69133             case e.ENTER:
69134                 if (g.isEditor && !g.editing) {
69135                     g.startEditing(r, c);
69136                     return;
69137                 }
69138                 break;
69139         }
69140
69141         if(newCell){
69142             // *** reassign r & c variables to newly-selected cell's row and column
69143             r = newCell[0];
69144             c = newCell[1];
69145
69146             this.select(r, c); // *** highlight newly-selected cell and update selection
69147
69148             if(g.isEditor && g.editing){ // *** handle tabbing while editorgrid is in edit mode
69149                 ae = g.activeEditor;
69150                 if(ae && ae.field.triggerBlur){
69151                     // *** if activeEditor is a TriggerField, explicitly call its triggerBlur() method
69152                     ae.field.triggerBlur();
69153                 }
69154                 g.startEditing(r, c);
69155             }
69156         }
69157     },
69158
69159     acceptsNav : function(row, col, cm){
69160         return !cm.isHidden(col) && cm.isCellEditable(col, row);
69161     }
69162 });/**
69163  * @class Ext.grid.EditorGridPanel
69164  * @extends Ext.grid.GridPanel
69165  * <p>This class extends the {@link Ext.grid.GridPanel GridPanel Class} to provide cell editing
69166  * on selected {@link Ext.grid.Column columns}. The editable columns are specified by providing
69167  * an {@link Ext.grid.ColumnModel#editor editor} in the {@link Ext.grid.Column column configuration}.</p>
69168  * <p>Editability of columns may be controlled programatically by inserting an implementation
69169  * of {@link Ext.grid.ColumnModel#isCellEditable isCellEditable} into the
69170  * {@link Ext.grid.ColumnModel ColumnModel}.</p>
69171  * <p>Editing is performed on the value of the <i>field</i> specified by the column's
69172  * <tt>{@link Ext.grid.ColumnModel#dataIndex dataIndex}</tt> in the backing {@link Ext.data.Store Store}
69173  * (so if you are using a {@link Ext.grid.ColumnModel#setRenderer renderer} in order to display
69174  * transformed data, this must be accounted for).</p>
69175  * <p>If a value-to-description mapping is used to render a column, then a {@link Ext.form.Field#ComboBox ComboBox}
69176  * which uses the same {@link Ext.form.Field#valueField value}-to-{@link Ext.form.Field#displayFieldField description}
69177  * mapping would be an appropriate editor.</p>
69178  * If there is a more complex mismatch between the visible data in the grid, and the editable data in
69179  * the {@link Edt.data.Store Store}, then code to transform the data both before and after editing can be
69180  * injected using the {@link #beforeedit} and {@link #afteredit} events.
69181  * @constructor
69182  * @param {Object} config The config object
69183  * @xtype editorgrid
69184  */
69185 Ext.grid.EditorGridPanel = Ext.extend(Ext.grid.GridPanel, {
69186     /**
69187      * @cfg {Number} clicksToEdit
69188      * <p>The number of clicks on a cell required to display the cell's editor (defaults to 2).</p>
69189      * <p>Setting this option to 'auto' means that mousedown <i>on the selected cell</i> starts
69190      * editing that cell.</p>
69191      */
69192     clicksToEdit: 2,
69193
69194     /**
69195     * @cfg {Boolean} forceValidation
69196     * True to force validation even if the value is unmodified (defaults to false)
69197     */
69198     forceValidation: false,
69199
69200     // private
69201     isEditor : true,
69202     // private
69203     detectEdit: false,
69204
69205     /**
69206      * @cfg {Boolean} autoEncode
69207      * True to automatically HTML encode and decode values pre and post edit (defaults to false)
69208      */
69209     autoEncode : false,
69210
69211     /**
69212      * @cfg {Boolean} trackMouseOver @hide
69213      */
69214     // private
69215     trackMouseOver: false, // causes very odd FF errors
69216
69217     // private
69218     initComponent : function(){
69219         Ext.grid.EditorGridPanel.superclass.initComponent.call(this);
69220
69221         if(!this.selModel){
69222             /**
69223              * @cfg {Object} selModel Any subclass of AbstractSelectionModel that will provide the selection model for
69224              * the grid (defaults to {@link Ext.grid.CellSelectionModel} if not specified).
69225              */
69226             this.selModel = new Ext.grid.CellSelectionModel();
69227         }
69228
69229         this.activeEditor = null;
69230
69231         this.addEvents(
69232             /**
69233              * @event beforeedit
69234              * Fires before cell editing is triggered. The edit event object has the following properties <br />
69235              * <ul style="padding:5px;padding-left:16px;">
69236              * <li>grid - This grid</li>
69237              * <li>record - The record being edited</li>
69238              * <li>field - The field name being edited</li>
69239              * <li>value - The value for the field being edited.</li>
69240              * <li>row - The grid row index</li>
69241              * <li>column - The grid column index</li>
69242              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
69243              * </ul>
69244              * @param {Object} e An edit event (see above for description)
69245              */
69246             "beforeedit",
69247             /**
69248              * @event afteredit
69249              * Fires after a cell is edited. The edit event object has the following properties <br />
69250              * <ul style="padding:5px;padding-left:16px;">
69251              * <li>grid - This grid</li>
69252              * <li>record - The record being edited</li>
69253              * <li>field - The field name being edited</li>
69254              * <li>value - The value being set</li>
69255              * <li>originalValue - The original value for the field, before the edit.</li>
69256              * <li>row - The grid row index</li>
69257              * <li>column - The grid column index</li>
69258              * </ul>
69259              *
69260              * <pre><code>
69261 grid.on('afteredit', afterEdit, this );
69262
69263 function afterEdit(e) {
69264     // execute an XHR to send/commit data to the server, in callback do (if successful):
69265     e.record.commit();
69266 };
69267              * </code></pre>
69268              * @param {Object} e An edit event (see above for description)
69269              */
69270             "afteredit",
69271             /**
69272              * @event validateedit
69273              * Fires after a cell is edited, but before the value is set in the record. Return false
69274              * to cancel the change. The edit event object has the following properties <br />
69275              * <ul style="padding:5px;padding-left:16px;">
69276              * <li>grid - This grid</li>
69277              * <li>record - The record being edited</li>
69278              * <li>field - The field name being edited</li>
69279              * <li>value - The value being set</li>
69280              * <li>originalValue - The original value for the field, before the edit.</li>
69281              * <li>row - The grid row index</li>
69282              * <li>column - The grid column index</li>
69283              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
69284              * </ul>
69285              * Usage example showing how to remove the red triangle (dirty record indicator) from some
69286              * records (not all).  By observing the grid's validateedit event, it can be cancelled if
69287              * the edit occurs on a targeted row (for example) and then setting the field's new value
69288              * in the Record directly:
69289              * <pre><code>
69290 grid.on('validateedit', function(e) {
69291   var myTargetRow = 6;
69292
69293   if (e.row == myTargetRow) {
69294     e.cancel = true;
69295     e.record.data[e.field] = e.value;
69296   }
69297 });
69298              * </code></pre>
69299              * @param {Object} e An edit event (see above for description)
69300              */
69301             "validateedit"
69302         );
69303     },
69304
69305     // private
69306     initEvents : function(){
69307         Ext.grid.EditorGridPanel.superclass.initEvents.call(this);
69308
69309         this.getGridEl().on('mousewheel', this.stopEditing.createDelegate(this, [true]), this);
69310         this.on('columnresize', this.stopEditing, this, [true]);
69311
69312         if(this.clicksToEdit == 1){
69313             this.on("cellclick", this.onCellDblClick, this);
69314         }else {
69315             var view = this.getView();
69316             if(this.clicksToEdit == 'auto' && view.mainBody){
69317                 view.mainBody.on('mousedown', this.onAutoEditClick, this);
69318             }
69319             this.on('celldblclick', this.onCellDblClick, this);
69320         }
69321     },
69322
69323     onResize : function(){
69324         Ext.grid.EditorGridPanel.superclass.onResize.apply(this, arguments);
69325         var ae = this.activeEditor;
69326         if(this.editing && ae){
69327             ae.realign(true);
69328         }
69329     },
69330
69331     // private
69332     onCellDblClick : function(g, row, col){
69333         this.startEditing(row, col);
69334     },
69335
69336     // private
69337     onAutoEditClick : function(e, t){
69338         if(e.button !== 0){
69339             return;
69340         }
69341         var row = this.view.findRowIndex(t),
69342             col = this.view.findCellIndex(t);
69343         if(row !== false && col !== false){
69344             this.stopEditing();
69345             if(this.selModel.getSelectedCell){ // cell sm
69346                 var sc = this.selModel.getSelectedCell();
69347                 if(sc && sc[0] === row && sc[1] === col){
69348                     this.startEditing(row, col);
69349                 }
69350             }else{
69351                 if(this.selModel.isSelected(row)){
69352                     this.startEditing(row, col);
69353                 }
69354             }
69355         }
69356     },
69357
69358     // private
69359     onEditComplete : function(ed, value, startValue){
69360         this.editing = false;
69361         this.activeEditor = null;
69362
69363         var r = ed.record,
69364             field = this.colModel.getDataIndex(ed.col);
69365         value = this.postEditValue(value, startValue, r, field);
69366         if(this.forceValidation === true || String(value) !== String(startValue)){
69367             var e = {
69368                 grid: this,
69369                 record: r,
69370                 field: field,
69371                 originalValue: startValue,
69372                 value: value,
69373                 row: ed.row,
69374                 column: ed.col,
69375                 cancel:false
69376             };
69377             if(this.fireEvent("validateedit", e) !== false && !e.cancel && String(value) !== String(startValue)){
69378                 r.set(field, e.value);
69379                 delete e.cancel;
69380                 this.fireEvent("afteredit", e);
69381             }
69382         }
69383         this.view.focusCell(ed.row, ed.col);
69384     },
69385
69386     /**
69387      * Starts editing the specified for the specified row/column
69388      * @param {Number} rowIndex
69389      * @param {Number} colIndex
69390      */
69391     startEditing : function(row, col){
69392         this.stopEditing();
69393         if(this.colModel.isCellEditable(col, row)){
69394             this.view.ensureVisible(row, col, true);
69395             var r = this.store.getAt(row),
69396                 field = this.colModel.getDataIndex(col),
69397                 e = {
69398                     grid: this,
69399                     record: r,
69400                     field: field,
69401                     value: r.data[field],
69402                     row: row,
69403                     column: col,
69404                     cancel:false
69405                 };
69406             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
69407                 this.editing = true;
69408                 var ed = this.colModel.getCellEditor(col, row);
69409                 if(!ed){
69410                     return;
69411                 }
69412                 if(!ed.rendered){
69413                     ed.parentEl = this.view.getEditorParent(ed);
69414                     ed.on({
69415                         scope: this,
69416                         render: {
69417                             fn: function(c){
69418                                 c.field.focus(false, true);
69419                             },
69420                             single: true,
69421                             scope: this
69422                         },
69423                         specialkey: function(field, e){
69424                             this.getSelectionModel().onEditorKey(field, e);
69425                         },
69426                         complete: this.onEditComplete,
69427                         canceledit: this.stopEditing.createDelegate(this, [true])
69428                     });
69429                 }
69430                 Ext.apply(ed, {
69431                     row     : row,
69432                     col     : col,
69433                     record  : r
69434                 });
69435                 this.lastEdit = {
69436                     row: row,
69437                     col: col
69438                 };
69439                 this.activeEditor = ed;
69440                 // Set the selectSameEditor flag if we are reusing the same editor again and
69441                 // need to prevent the editor from firing onBlur on itself.
69442                 ed.selectSameEditor = (this.activeEditor == this.lastActiveEditor);
69443                 var v = this.preEditValue(r, field);
69444                 ed.startEdit(this.view.getCell(row, col).firstChild, Ext.isDefined(v) ? v : '');
69445
69446                 // Clear the selectSameEditor flag
69447                 (function(){
69448                     delete ed.selectSameEditor;
69449                 }).defer(50);
69450             }
69451         }
69452     },
69453
69454     // private
69455     preEditValue : function(r, field){
69456         var value = r.data[field];
69457         return this.autoEncode && Ext.isString(value) ? Ext.util.Format.htmlDecode(value) : value;
69458     },
69459
69460     // private
69461     postEditValue : function(value, originalValue, r, field){
69462         return this.autoEncode && Ext.isString(value) ? Ext.util.Format.htmlEncode(value) : value;
69463     },
69464
69465     /**
69466      * Stops any active editing
69467      * @param {Boolean} cancel (optional) True to cancel any changes
69468      */
69469     stopEditing : function(cancel){
69470         if(this.editing){
69471             // Store the lastActiveEditor to check if it is changing
69472             var ae = this.lastActiveEditor = this.activeEditor;
69473             if(ae){
69474                 ae[cancel === true ? 'cancelEdit' : 'completeEdit']();
69475                 this.view.focusCell(ae.row, ae.col);
69476             }
69477             this.activeEditor = null;
69478         }
69479         this.editing = false;
69480     }
69481 });
69482 Ext.reg('editorgrid', Ext.grid.EditorGridPanel);// private
69483 // This is a support class used internally by the Grid components
69484 Ext.grid.GridEditor = function(field, config){
69485     Ext.grid.GridEditor.superclass.constructor.call(this, field, config);
69486     field.monitorTab = false;
69487 };
69488
69489 Ext.extend(Ext.grid.GridEditor, Ext.Editor, {
69490     alignment: "tl-tl",
69491     autoSize: "width",
69492     hideEl : false,
69493     cls: "x-small-editor x-grid-editor",
69494     shim:false,
69495     shadow:false
69496 });/**
69497  * @class Ext.grid.PropertyRecord
69498  * A specific {@link Ext.data.Record} type that represents a name/value pair and is made to work with the
69499  * {@link Ext.grid.PropertyGrid}.  Typically, PropertyRecords do not need to be created directly as they can be
69500  * created implicitly by simply using the appropriate data configs either via the {@link Ext.grid.PropertyGrid#source}
69501  * config property or by calling {@link Ext.grid.PropertyGrid#setSource}.  However, if the need arises, these records
69502  * can also be created explicitly as shwon below.  Example usage:
69503  * <pre><code>
69504 var rec = new Ext.grid.PropertyRecord({
69505     name: 'Birthday',
69506     value: new Date(Date.parse('05/26/1972'))
69507 });
69508 // Add record to an already populated grid
69509 grid.store.addSorted(rec);
69510 </code></pre>
69511  * @constructor
69512  * @param {Object} config A data object in the format: {name: [name], value: [value]}.  The specified value's type
69513  * will be read automatically by the grid to determine the type of editor to use when displaying it.
69514  */
69515 Ext.grid.PropertyRecord = Ext.data.Record.create([
69516     {name:'name',type:'string'}, 'value'
69517 ]);
69518
69519 /**
69520  * @class Ext.grid.PropertyStore
69521  * @extends Ext.util.Observable
69522  * A custom wrapper for the {@link Ext.grid.PropertyGrid}'s {@link Ext.data.Store}. This class handles the mapping
69523  * between the custom data source objects supported by the grid and the {@link Ext.grid.PropertyRecord} format
69524  * required for compatibility with the underlying store. Generally this class should not need to be used directly --
69525  * the grid's data should be accessed from the underlying store via the {@link #store} property.
69526  * @constructor
69527  * @param {Ext.grid.Grid} grid The grid this store will be bound to
69528  * @param {Object} source The source data config object
69529  */
69530 Ext.grid.PropertyStore = Ext.extend(Ext.util.Observable, {
69531     
69532     constructor : function(grid, source){
69533         this.grid = grid;
69534         this.store = new Ext.data.Store({
69535             recordType : Ext.grid.PropertyRecord
69536         });
69537         this.store.on('update', this.onUpdate,  this);
69538         if(source){
69539             this.setSource(source);
69540         }
69541         Ext.grid.PropertyStore.superclass.constructor.call(this);    
69542     },
69543     
69544     // protected - should only be called by the grid.  Use grid.setSource instead.
69545     setSource : function(o){
69546         this.source = o;
69547         this.store.removeAll();
69548         var data = [];
69549         for(var k in o){
69550             if(this.isEditableValue(o[k])){
69551                 data.push(new Ext.grid.PropertyRecord({name: k, value: o[k]}, k));
69552             }
69553         }
69554         this.store.loadRecords({records: data}, {}, true);
69555     },
69556
69557     // private
69558     onUpdate : function(ds, record, type){
69559         if(type == Ext.data.Record.EDIT){
69560             var v = record.data.value;
69561             var oldValue = record.modified.value;
69562             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
69563                 this.source[record.id] = v;
69564                 record.commit();
69565                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
69566             }else{
69567                 record.reject();
69568             }
69569         }
69570     },
69571
69572     // private
69573     getProperty : function(row){
69574        return this.store.getAt(row);
69575     },
69576
69577     // private
69578     isEditableValue: function(val){
69579         return Ext.isPrimitive(val) || Ext.isDate(val);
69580     },
69581
69582     // private
69583     setValue : function(prop, value, create){
69584         var r = this.getRec(prop);
69585         if(r){
69586             r.set('value', value);
69587             this.source[prop] = value;
69588         }else if(create){
69589             // only create if specified.
69590             this.source[prop] = value;
69591             r = new Ext.grid.PropertyRecord({name: prop, value: value}, prop);
69592             this.store.add(r);
69593
69594         }
69595     },
69596     
69597     // private
69598     remove : function(prop){
69599         var r = this.getRec(prop);
69600         if(r){
69601             this.store.remove(r);
69602             delete this.source[prop];
69603         }
69604     },
69605     
69606     // private
69607     getRec : function(prop){
69608         return this.store.getById(prop);
69609     },
69610
69611     // protected - should only be called by the grid.  Use grid.getSource instead.
69612     getSource : function(){
69613         return this.source;
69614     }
69615 });
69616
69617 /**
69618  * @class Ext.grid.PropertyColumnModel
69619  * @extends Ext.grid.ColumnModel
69620  * A custom column model for the {@link Ext.grid.PropertyGrid}.  Generally it should not need to be used directly.
69621  * @constructor
69622  * @param {Ext.grid.Grid} grid The grid this store will be bound to
69623  * @param {Object} source The source data config object
69624  */
69625 Ext.grid.PropertyColumnModel = Ext.extend(Ext.grid.ColumnModel, {
69626     // private - strings used for locale support
69627     nameText : 'Name',
69628     valueText : 'Value',
69629     dateFormat : 'm/j/Y',
69630     trueText: 'true',
69631     falseText: 'false',
69632     
69633     constructor : function(grid, store){
69634         var g = Ext.grid,
69635                 f = Ext.form;
69636                 
69637             this.grid = grid;
69638             g.PropertyColumnModel.superclass.constructor.call(this, [
69639                 {header: this.nameText, width:50, sortable: true, dataIndex:'name', id: 'name', menuDisabled:true},
69640                 {header: this.valueText, width:50, resizable:false, dataIndex: 'value', id: 'value', menuDisabled:true}
69641             ]);
69642             this.store = store;
69643         
69644             var bfield = new f.Field({
69645                 autoCreate: {tag: 'select', children: [
69646                     {tag: 'option', value: 'true', html: this.trueText},
69647                     {tag: 'option', value: 'false', html: this.falseText}
69648                 ]},
69649                 getValue : function(){
69650                     return this.el.dom.value == 'true';
69651                 }
69652             });
69653             this.editors = {
69654                 'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
69655                 'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
69656                 'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
69657                 'boolean' : new g.GridEditor(bfield, {
69658                     autoSize: 'both'
69659                 })
69660             };
69661             this.renderCellDelegate = this.renderCell.createDelegate(this);
69662             this.renderPropDelegate = this.renderProp.createDelegate(this);
69663     },
69664
69665     // private
69666     renderDate : function(dateVal){
69667         return dateVal.dateFormat(this.dateFormat);
69668     },
69669
69670     // private
69671     renderBool : function(bVal){
69672         return this[bVal ? 'trueText' : 'falseText'];
69673     },
69674
69675     // private
69676     isCellEditable : function(colIndex, rowIndex){
69677         return colIndex == 1;
69678     },
69679
69680     // private
69681     getRenderer : function(col){
69682         return col == 1 ?
69683             this.renderCellDelegate : this.renderPropDelegate;
69684     },
69685
69686     // private
69687     renderProp : function(v){
69688         return this.getPropertyName(v);
69689     },
69690
69691     // private
69692     renderCell : function(val, meta, rec){
69693         var renderer = this.grid.customRenderers[rec.get('name')];
69694         if(renderer){
69695             return renderer.apply(this, arguments);
69696         }
69697         var rv = val;
69698         if(Ext.isDate(val)){
69699             rv = this.renderDate(val);
69700         }else if(typeof val == 'boolean'){
69701             rv = this.renderBool(val);
69702         }
69703         return Ext.util.Format.htmlEncode(rv);
69704     },
69705
69706     // private
69707     getPropertyName : function(name){
69708         var pn = this.grid.propertyNames;
69709         return pn && pn[name] ? pn[name] : name;
69710     },
69711
69712     // private
69713     getCellEditor : function(colIndex, rowIndex){
69714         var p = this.store.getProperty(rowIndex),
69715             n = p.data.name, 
69716             val = p.data.value;
69717         if(this.grid.customEditors[n]){
69718             return this.grid.customEditors[n];
69719         }
69720         if(Ext.isDate(val)){
69721             return this.editors.date;
69722         }else if(typeof val == 'number'){
69723             return this.editors.number;
69724         }else if(typeof val == 'boolean'){
69725             return this.editors['boolean'];
69726         }else{
69727             return this.editors.string;
69728         }
69729     },
69730
69731     // inherit docs
69732     destroy : function(){
69733         Ext.grid.PropertyColumnModel.superclass.destroy.call(this);
69734         for(var ed in this.editors){
69735             Ext.destroy(this.editors[ed]);
69736         }
69737     }
69738 });
69739
69740 /**
69741  * @class Ext.grid.PropertyGrid
69742  * @extends Ext.grid.EditorGridPanel
69743  * A specialized grid implementation intended to mimic the traditional property grid as typically seen in
69744  * development IDEs.  Each row in the grid represents a property of some object, and the data is stored
69745  * as a set of name/value pairs in {@link Ext.grid.PropertyRecord}s.  Example usage:
69746  * <pre><code>
69747 var grid = new Ext.grid.PropertyGrid({
69748     title: 'Properties Grid',
69749     autoHeight: true,
69750     width: 300,
69751     renderTo: 'grid-ct',
69752     source: {
69753         "(name)": "My Object",
69754         "Created": new Date(Date.parse('10/15/2006')),
69755         "Available": false,
69756         "Version": .01,
69757         "Description": "A test object"
69758     }
69759 });
69760 </code></pre>
69761  * @constructor
69762  * @param {Object} config The grid config object
69763  */
69764 Ext.grid.PropertyGrid = Ext.extend(Ext.grid.EditorGridPanel, {
69765     /**
69766     * @cfg {Object} propertyNames An object containing property name/display name pairs.
69767     * If specified, the display name will be shown in the name column instead of the property name.
69768     */
69769     /**
69770     * @cfg {Object} source A data object to use as the data source of the grid (see {@link #setSource} for details).
69771     */
69772     /**
69773     * @cfg {Object} customEditors An object containing name/value pairs of custom editor type definitions that allow
69774     * the grid to support additional types of editable fields.  By default, the grid supports strongly-typed editing
69775     * of strings, dates, numbers and booleans using built-in form editors, but any custom type can be supported and
69776     * associated with a custom input control by specifying a custom editor.  The name of the editor
69777     * type should correspond with the name of the property that will use the editor.  Example usage:
69778     * <pre><code>
69779 var grid = new Ext.grid.PropertyGrid({
69780     ...
69781     customEditors: {
69782         'Start Time': new Ext.grid.GridEditor(new Ext.form.TimeField({selectOnFocus:true}))
69783     },
69784     source: {
69785         'Start Time': '10:00 AM'
69786     }
69787 });
69788 </code></pre>
69789     */
69790     /**
69791     * @cfg {Object} source A data object to use as the data source of the grid (see {@link #setSource} for details).
69792     */
69793     /**
69794     * @cfg {Object} customRenderers An object containing name/value pairs of custom renderer type definitions that allow
69795     * the grid to support custom rendering of fields.  By default, the grid supports strongly-typed rendering
69796     * of strings, dates, numbers and booleans using built-in form editors, but any custom type can be supported and
69797     * associated with the type of the value.  The name of the renderer type should correspond with the name of the property
69798     * that it will render.  Example usage:
69799     * <pre><code>
69800 var grid = new Ext.grid.PropertyGrid({
69801     ...
69802     customRenderers: {
69803         Available: function(v){
69804             if(v){
69805                 return '<span style="color: green;">Yes</span>';
69806             }else{
69807                 return '<span style="color: red;">No</span>';
69808             }
69809         }
69810     },
69811     source: {
69812         Available: true
69813     }
69814 });
69815 </code></pre>
69816     */
69817
69818     // private config overrides
69819     enableColumnMove:false,
69820     stripeRows:false,
69821     trackMouseOver: false,
69822     clicksToEdit:1,
69823     enableHdMenu : false,
69824     viewConfig : {
69825         forceFit:true
69826     },
69827
69828     // private
69829     initComponent : function(){
69830         this.customRenderers = this.customRenderers || {};
69831         this.customEditors = this.customEditors || {};
69832         this.lastEditRow = null;
69833         var store = new Ext.grid.PropertyStore(this);
69834         this.propStore = store;
69835         var cm = new Ext.grid.PropertyColumnModel(this, store);
69836         store.store.sort('name', 'ASC');
69837         this.addEvents(
69838             /**
69839              * @event beforepropertychange
69840              * Fires before a property value changes.  Handlers can return false to cancel the property change
69841              * (this will internally call {@link Ext.data.Record#reject} on the property's record).
69842              * @param {Object} source The source data object for the grid (corresponds to the same object passed in
69843              * as the {@link #source} config property).
69844              * @param {String} recordId The record's id in the data store
69845              * @param {Mixed} value The current edited property value
69846              * @param {Mixed} oldValue The original property value prior to editing
69847              */
69848             'beforepropertychange',
69849             /**
69850              * @event propertychange
69851              * Fires after a property value has changed.
69852              * @param {Object} source The source data object for the grid (corresponds to the same object passed in
69853              * as the {@link #source} config property).
69854              * @param {String} recordId The record's id in the data store
69855              * @param {Mixed} value The current edited property value
69856              * @param {Mixed} oldValue The original property value prior to editing
69857              */
69858             'propertychange'
69859         );
69860         this.cm = cm;
69861         this.ds = store.store;
69862         Ext.grid.PropertyGrid.superclass.initComponent.call(this);
69863
69864                 this.mon(this.selModel, 'beforecellselect', function(sm, rowIndex, colIndex){
69865             if(colIndex === 0){
69866                 this.startEditing.defer(200, this, [rowIndex, 1]);
69867                 return false;
69868             }
69869         }, this);
69870     },
69871
69872     // private
69873     onRender : function(){
69874         Ext.grid.PropertyGrid.superclass.onRender.apply(this, arguments);
69875
69876         this.getGridEl().addClass('x-props-grid');
69877     },
69878
69879     // private
69880     afterRender: function(){
69881         Ext.grid.PropertyGrid.superclass.afterRender.apply(this, arguments);
69882         if(this.source){
69883             this.setSource(this.source);
69884         }
69885     },
69886
69887     /**
69888      * Sets the source data object containing the property data.  The data object can contain one or more name/value
69889      * pairs representing all of the properties of an object to display in the grid, and this data will automatically
69890      * be loaded into the grid's {@link #store}.  The values should be supplied in the proper data type if needed,
69891      * otherwise string type will be assumed.  If the grid already contains data, this method will replace any
69892      * existing data.  See also the {@link #source} config value.  Example usage:
69893      * <pre><code>
69894 grid.setSource({
69895     "(name)": "My Object",
69896     "Created": new Date(Date.parse('10/15/2006')),  // date type
69897     "Available": false,  // boolean type
69898     "Version": .01,      // decimal type
69899     "Description": "A test object"
69900 });
69901 </code></pre>
69902      * @param {Object} source The data object
69903      */
69904     setSource : function(source){
69905         this.propStore.setSource(source);
69906     },
69907
69908     /**
69909      * Gets the source data object containing the property data.  See {@link #setSource} for details regarding the
69910      * format of the data object.
69911      * @return {Object} The data object
69912      */
69913     getSource : function(){
69914         return this.propStore.getSource();
69915     },
69916     
69917     /**
69918      * Sets the value of a property.
69919      * @param {String} prop The name of the property to set
69920      * @param {Mixed} value The value to test
69921      * @param {Boolean} create (Optional) True to create the property if it doesn't already exist. Defaults to <tt>false</tt>.
69922      */
69923     setProperty : function(prop, value, create){
69924         this.propStore.setValue(prop, value, create);    
69925     },
69926     
69927     /**
69928      * Removes a property from the grid.
69929      * @param {String} prop The name of the property to remove
69930      */
69931     removeProperty : function(prop){
69932         this.propStore.remove(prop);
69933     }
69934
69935     /**
69936      * @cfg store
69937      * @hide
69938      */
69939     /**
69940      * @cfg colModel
69941      * @hide
69942      */
69943     /**
69944      * @cfg cm
69945      * @hide
69946      */
69947     /**
69948      * @cfg columns
69949      * @hide
69950      */
69951 });
69952 Ext.reg("propertygrid", Ext.grid.PropertyGrid);
69953 /**\r
69954  * @class Ext.grid.GroupingView\r
69955  * @extends Ext.grid.GridView\r
69956  * Adds the ability for single level grouping to the grid. A {@link Ext.data.GroupingStore GroupingStore}\r
69957  * must be used to enable grouping.  Some grouping characteristics may also be configured at the\r
69958  * {@link Ext.grid.Column Column level}<div class="mdetail-params"><ul>\r
69959  * <li><code>{@link Ext.grid.Column#emptyGroupText emptyGroupText}</li>\r
69960  * <li><code>{@link Ext.grid.Column#groupable groupable}</li>\r
69961  * <li><code>{@link Ext.grid.Column#groupName groupName}</li>\r
69962  * <li><code>{@link Ext.grid.Column#groupRender groupRender}</li>\r
69963  * </ul></div>\r
69964  * <p>Sample usage:</p>\r
69965  * <pre><code>\r
69966 var grid = new Ext.grid.GridPanel({\r
69967     // A groupingStore is required for a GroupingView\r
69968     store: new {@link Ext.data.GroupingStore}({\r
69969         autoDestroy: true,\r
69970         reader: reader,\r
69971         data: xg.dummyData,\r
69972         sortInfo: {field: 'company', direction: 'ASC'},\r
69973         {@link Ext.data.GroupingStore#groupOnSort groupOnSort}: true,\r
69974         {@link Ext.data.GroupingStore#remoteGroup remoteGroup}: true,\r
69975         {@link Ext.data.GroupingStore#groupField groupField}: 'industry'\r
69976     }),\r
69977     colModel: new {@link Ext.grid.ColumnModel}({\r
69978         columns:[\r
69979             {id:'company',header: 'Company', width: 60, dataIndex: 'company'},\r
69980             // {@link Ext.grid.Column#groupable groupable}, {@link Ext.grid.Column#groupName groupName}, {@link Ext.grid.Column#groupRender groupRender} are also configurable at column level\r
69981             {header: 'Price', renderer: Ext.util.Format.usMoney, dataIndex: 'price', {@link Ext.grid.Column#groupable groupable}: false},\r
69982             {header: 'Change', dataIndex: 'change', renderer: Ext.util.Format.usMoney},\r
69983             {header: 'Industry', dataIndex: 'industry'},\r
69984             {header: 'Last Updated', renderer: Ext.util.Format.dateRenderer('m/d/Y'), dataIndex: 'lastChange'}\r
69985         ],\r
69986         defaults: {\r
69987             sortable: true,\r
69988             menuDisabled: false,\r
69989             width: 20\r
69990         }\r
69991     }),\r
69992 \r
69993     view: new Ext.grid.GroupingView({\r
69994         {@link Ext.grid.GridView#forceFit forceFit}: true,\r
69995         // custom grouping text template to display the number of items per group\r
69996         {@link #groupTextTpl}: '{text} ({[values.rs.length]} {[values.rs.length > 1 ? "Items" : "Item"]})'\r
69997     }),\r
69998 \r
69999     frame:true,\r
70000     width: 700,\r
70001     height: 450,\r
70002     collapsible: true,\r
70003     animCollapse: false,\r
70004     title: 'Grouping Example',\r
70005     iconCls: 'icon-grid',\r
70006     renderTo: document.body\r
70007 });\r
70008  * </code></pre>\r
70009  * @constructor\r
70010  * @param {Object} config\r
70011  */\r
70012 Ext.grid.GroupingView = Ext.extend(Ext.grid.GridView, {\r
70013 \r
70014     /**\r
70015      * @cfg {String} groupByText Text displayed in the grid header menu for grouping by a column\r
70016      * (defaults to 'Group By This Field').\r
70017      */\r
70018     groupByText : 'Group By This Field',\r
70019     /**\r
70020      * @cfg {String} showGroupsText Text displayed in the grid header for enabling/disabling grouping\r
70021      * (defaults to 'Show in Groups').\r
70022      */\r
70023     showGroupsText : 'Show in Groups',\r
70024     /**\r
70025      * @cfg {Boolean} hideGroupedColumn <tt>true</tt> to hide the column that is currently grouped (defaults to <tt>false</tt>)\r
70026      */\r
70027     hideGroupedColumn : false,\r
70028     /**\r
70029      * @cfg {Boolean} showGroupName If <tt>true</tt> will display a prefix plus a ': ' before the group field value\r
70030      * in the group header line.  The prefix will consist of the <tt><b>{@link Ext.grid.Column#groupName groupName}</b></tt>\r
70031      * (or the configured <tt><b>{@link Ext.grid.Column#header header}</b></tt> if not provided) configured in the\r
70032      * {@link Ext.grid.Column} for each set of grouped rows (defaults to <tt>true</tt>).\r
70033      */\r
70034     showGroupName : true,\r
70035     /**\r
70036      * @cfg {Boolean} startCollapsed <tt>true</tt> to start all groups collapsed (defaults to <tt>false</tt>)\r
70037      */\r
70038     startCollapsed : false,\r
70039     /**\r
70040      * @cfg {Boolean} enableGrouping <tt>false</tt> to disable grouping functionality (defaults to <tt>true</tt>)\r
70041      */\r
70042     enableGrouping : true,\r
70043     /**\r
70044      * @cfg {Boolean} enableGroupingMenu <tt>true</tt> to enable the grouping control in the column menu (defaults to <tt>true</tt>)\r
70045      */\r
70046     enableGroupingMenu : true,\r
70047     /**\r
70048      * @cfg {Boolean} enableNoGroups <tt>true</tt> to allow the user to turn off grouping (defaults to <tt>true</tt>)\r
70049      */\r
70050     enableNoGroups : true,\r
70051     /**\r
70052      * @cfg {String} emptyGroupText The text to display when there is an empty group value (defaults to <tt>'(None)'</tt>).\r
70053      * May also be specified per column, see {@link Ext.grid.Column}.{@link Ext.grid.Column#emptyGroupText emptyGroupText}.\r
70054      */\r
70055     emptyGroupText : '(None)',\r
70056     /**\r
70057      * @cfg {Boolean} ignoreAdd <tt>true</tt> to skip refreshing the view when new rows are added (defaults to <tt>false</tt>)\r
70058      */\r
70059     ignoreAdd : false,\r
70060     /**\r
70061      * @cfg {String} groupTextTpl The template used to render the group header (defaults to <tt>'{text}'</tt>).\r
70062      * This is used to format an object which contains the following properties:\r
70063      * <div class="mdetail-params"><ul>\r
70064      * <li><b>group</b> : String<p class="sub-desc">The <i>rendered</i> value of the group field.\r
70065      * By default this is the unchanged value of the group field. If a <tt><b>{@link Ext.grid.Column#groupRenderer groupRenderer}</b></tt>\r
70066      * is specified, it is the result of a call to that function.</p></li>\r
70067      * <li><b>gvalue</b> : Object<p class="sub-desc">The <i>raw</i> value of the group field.</p></li>\r
70068      * <li><b>text</b> : String<p class="sub-desc">The configured header (as described in <tt>{@link #showGroupName})</tt>\r
70069      * if <tt>{@link #showGroupName}</tt> is <tt>true</tt>) plus the <i>rendered</i> group field value.</p></li>\r
70070      * <li><b>groupId</b> : String<p class="sub-desc">A unique, generated ID which is applied to the\r
70071      * View Element which contains the group.</p></li>\r
70072      * <li><b>startRow</b> : Number<p class="sub-desc">The row index of the Record which caused group change.</p></li>\r
70073      * <li><b>rs</b> : Array<p class="sub-desc">Contains a single element: The Record providing the data\r
70074      * for the row which caused group change.</p></li>\r
70075      * <li><b>cls</b> : String<p class="sub-desc">The generated class name string to apply to the group header Element.</p></li>\r
70076      * <li><b>style</b> : String<p class="sub-desc">The inline style rules to apply to the group header Element.</p></li>\r
70077      * </ul></div></p>\r
70078      * See {@link Ext.XTemplate} for information on how to format data using a template. Possible usage:<pre><code>\r
70079 var grid = new Ext.grid.GridPanel({\r
70080     ...\r
70081     view: new Ext.grid.GroupingView({\r
70082         groupTextTpl: '{text} ({[values.rs.length]} {[values.rs.length > 1 ? "Items" : "Item"]})'\r
70083     }),\r
70084 });\r
70085      * </code></pre>\r
70086      */\r
70087     groupTextTpl : '{text}',\r
70088 \r
70089     /**\r
70090      * @cfg {String} groupMode Indicates how to construct the group identifier. <tt>'value'</tt> constructs the id using\r
70091      * raw value, <tt>'display'</tt> constructs the id using the rendered value. Defaults to <tt>'value'</tt>.\r
70092      */\r
70093     groupMode: 'value',\r
70094 \r
70095     /**\r
70096      * @cfg {Function} groupRenderer This property must be configured in the {@link Ext.grid.Column} for\r
70097      * each column.\r
70098      */\r
70099 \r
70100     // private\r
70101     gidSeed : 1000,\r
70102 \r
70103     // private\r
70104     initTemplates : function(){\r
70105         Ext.grid.GroupingView.superclass.initTemplates.call(this);\r
70106         this.state = {};\r
70107 \r
70108         var sm = this.grid.getSelectionModel();\r
70109         sm.on(sm.selectRow ? 'beforerowselect' : 'beforecellselect',\r
70110                 this.onBeforeRowSelect, this);\r
70111 \r
70112         if(!this.startGroup){\r
70113             this.startGroup = new Ext.XTemplate(\r
70114                 '<div id="{groupId}" class="x-grid-group {cls}">',\r
70115                     '<div id="{groupId}-hd" class="x-grid-group-hd" style="{style}"><div class="x-grid-group-title">', this.groupTextTpl ,'</div></div>',\r
70116                     '<div id="{groupId}-bd" class="x-grid-group-body">'\r
70117             );\r
70118         }\r
70119         this.startGroup.compile();\r
70120         if(!this.endGroup){\r
70121             this.endGroup = '</div></div>';\r
70122         }\r
70123 \r
70124         this.endGroup = '</div></div>';\r
70125     },\r
70126 \r
70127     // private\r
70128     findGroup : function(el){\r
70129         return Ext.fly(el).up('.x-grid-group', this.mainBody.dom);\r
70130     },\r
70131 \r
70132     // private\r
70133     getGroups : function(){\r
70134         return this.hasRows() ? this.mainBody.dom.childNodes : [];\r
70135     },\r
70136 \r
70137     // private\r
70138     onAdd : function(){\r
70139         if(this.canGroup() && !this.ignoreAdd){\r
70140             var ss = this.getScrollState();\r
70141             this.refresh();\r
70142             this.restoreScroll(ss);\r
70143         }else if(!this.canGroup()){\r
70144             Ext.grid.GroupingView.superclass.onAdd.apply(this, arguments);\r
70145         }\r
70146     },\r
70147 \r
70148     // private\r
70149     onRemove : function(ds, record, index, isUpdate){\r
70150         Ext.grid.GroupingView.superclass.onRemove.apply(this, arguments);\r
70151         var g = document.getElementById(record._groupId);\r
70152         if(g && g.childNodes[1].childNodes.length < 1){\r
70153             Ext.removeNode(g);\r
70154         }\r
70155         this.applyEmptyText();\r
70156     },\r
70157 \r
70158     // private\r
70159     refreshRow : function(record){\r
70160         if(this.ds.getCount()==1){\r
70161             this.refresh();\r
70162         }else{\r
70163             this.isUpdating = true;\r
70164             Ext.grid.GroupingView.superclass.refreshRow.apply(this, arguments);\r
70165             this.isUpdating = false;\r
70166         }\r
70167     },\r
70168 \r
70169     // private\r
70170     beforeMenuShow : function(){\r
70171         var item, items = this.hmenu.items, disabled = this.cm.config[this.hdCtxIndex].groupable === false;\r
70172         if((item = items.get('groupBy'))){\r
70173             item.setDisabled(disabled);\r
70174         }\r
70175         if((item = items.get('showGroups'))){\r
70176             item.setDisabled(disabled);\r
70177             item.setChecked(this.enableGrouping, true);\r
70178         }\r
70179     },\r
70180 \r
70181     // private\r
70182     renderUI : function(){\r
70183         Ext.grid.GroupingView.superclass.renderUI.call(this);\r
70184         this.mainBody.on('mousedown', this.interceptMouse, this);\r
70185 \r
70186         if(this.enableGroupingMenu && this.hmenu){\r
70187             this.hmenu.add('-',{\r
70188                 itemId:'groupBy',\r
70189                 text: this.groupByText,\r
70190                 handler: this.onGroupByClick,\r
70191                 scope: this,\r
70192                 iconCls:'x-group-by-icon'\r
70193             });\r
70194             if(this.enableNoGroups){\r
70195                 this.hmenu.add({\r
70196                     itemId:'showGroups',\r
70197                     text: this.showGroupsText,\r
70198                     checked: true,\r
70199                     checkHandler: this.onShowGroupsClick,\r
70200                     scope: this\r
70201                 });\r
70202             }\r
70203             this.hmenu.on('beforeshow', this.beforeMenuShow, this);\r
70204         }\r
70205     },\r
70206 \r
70207     processEvent: function(name, e){\r
70208         var hd = e.getTarget('.x-grid-group-hd', this.mainBody);\r
70209         if(hd){\r
70210             // group value is at the end of the string\r
70211             var field = this.getGroupField(),\r
70212                 prefix = this.getPrefix(field),\r
70213                 groupValue = hd.id.substring(prefix.length);\r
70214 \r
70215             // remove trailing '-hd'\r
70216             groupValue = groupValue.substr(0, groupValue.length - 3);\r
70217             if(groupValue){\r
70218                 this.grid.fireEvent('group' + name, this.grid, field, groupValue, e);\r
70219             }\r
70220         }\r
70221 \r
70222     },\r
70223 \r
70224     // private\r
70225     onGroupByClick : function(){\r
70226         this.enableGrouping = true;\r
70227         this.grid.store.groupBy(this.cm.getDataIndex(this.hdCtxIndex));\r
70228         this.grid.fireEvent('groupchange', this, this.grid.store.getGroupState());\r
70229         this.beforeMenuShow(); // Make sure the checkboxes get properly set when changing groups\r
70230         this.refresh();\r
70231     },\r
70232 \r
70233     // private\r
70234     onShowGroupsClick : function(mi, checked){\r
70235     this.enableGrouping = checked;\r
70236         if(checked){\r
70237             this.onGroupByClick();\r
70238         }else{\r
70239             this.grid.store.clearGrouping();\r
70240             this.grid.fireEvent('groupchange', this, null);\r
70241         }\r
70242     },\r
70243 \r
70244     /**\r
70245      * Toggle the group that contains the specific row.\r
70246      * @param {Number} rowIndex The row inside the group\r
70247      * @param {Boolean} expanded (optional)\r
70248      */\r
70249     toggleRowIndex : function(rowIndex, expanded){\r
70250         if(!this.canGroup()){\r
70251             return;\r
70252         }\r
70253         var row = this.getRow(rowIndex);\r
70254         if(row){\r
70255             this.toggleGroup(this.findGroup(row), expanded);\r
70256         }\r
70257     },\r
70258 \r
70259     /**\r
70260      * Toggles the specified group if no value is passed, otherwise sets the expanded state of the group to the value passed.\r
70261      * @param {String} groupId The groupId assigned to the group (see getGroupId)\r
70262      * @param {Boolean} expanded (optional)\r
70263      */\r
70264     toggleGroup : function(group, expanded){\r
70265         var gel = Ext.get(group);\r
70266         expanded = Ext.isDefined(expanded) ? expanded : gel.hasClass('x-grid-group-collapsed');\r
70267         if(this.state[gel.id] !== expanded){\r
70268             this.grid.stopEditing(true);\r
70269             this.state[gel.id] = expanded;\r
70270             gel[expanded ? 'removeClass' : 'addClass']('x-grid-group-collapsed');\r
70271         }\r
70272     },\r
70273 \r
70274     /**\r
70275      * Toggles all groups if no value is passed, otherwise sets the expanded state of all groups to the value passed.\r
70276      * @param {Boolean} expanded (optional)\r
70277      */\r
70278     toggleAllGroups : function(expanded){\r
70279         var groups = this.getGroups();\r
70280         for(var i = 0, len = groups.length; i < len; i++){\r
70281             this.toggleGroup(groups[i], expanded);\r
70282         }\r
70283     },\r
70284 \r
70285     /**\r
70286      * Expands all grouped rows.\r
70287      */\r
70288     expandAllGroups : function(){\r
70289         this.toggleAllGroups(true);\r
70290     },\r
70291 \r
70292     /**\r
70293      * Collapses all grouped rows.\r
70294      */\r
70295     collapseAllGroups : function(){\r
70296         this.toggleAllGroups(false);\r
70297     },\r
70298 \r
70299     // private\r
70300     interceptMouse : function(e){\r
70301         var hd = e.getTarget('.x-grid-group-hd', this.mainBody);\r
70302         if(hd){\r
70303             e.stopEvent();\r
70304             this.toggleGroup(hd.parentNode);\r
70305         }\r
70306     },\r
70307 \r
70308     // private\r
70309     getGroup : function(v, r, groupRenderer, rowIndex, colIndex, ds){\r
70310         var g = groupRenderer ? groupRenderer(v, {}, r, rowIndex, colIndex, ds) : String(v);\r
70311         if(g === '' || g === '&#160;'){\r
70312             g = this.cm.config[colIndex].emptyGroupText || this.emptyGroupText;\r
70313         }\r
70314         return g;\r
70315     },\r
70316 \r
70317     // private\r
70318     getGroupField : function(){\r
70319         return this.grid.store.getGroupState();\r
70320     },\r
70321 \r
70322     // private\r
70323     afterRender : function(){\r
70324         if(!this.ds || !this.cm){\r
70325             return;\r
70326         }\r
70327         Ext.grid.GroupingView.superclass.afterRender.call(this);\r
70328         if(this.grid.deferRowRender){\r
70329             this.updateGroupWidths();\r
70330         }\r
70331     },\r
70332 \r
70333     // private\r
70334     renderRows : function(){\r
70335         var groupField = this.getGroupField();\r
70336         var eg = !!groupField;\r
70337         // if they turned off grouping and the last grouped field is hidden\r
70338         if(this.hideGroupedColumn) {\r
70339             var colIndex = this.cm.findColumnIndex(groupField),\r
70340                 hasLastGroupField = Ext.isDefined(this.lastGroupField);\r
70341             if(!eg && hasLastGroupField){\r
70342                 this.mainBody.update('');\r
70343                 this.cm.setHidden(this.cm.findColumnIndex(this.lastGroupField), false);\r
70344                 delete this.lastGroupField;\r
70345             }else if (eg && !hasLastGroupField){\r
70346                 this.lastGroupField = groupField;\r
70347                 this.cm.setHidden(colIndex, true);\r
70348             }else if (eg && hasLastGroupField && groupField !== this.lastGroupField) {\r
70349                 this.mainBody.update('');\r
70350                 var oldIndex = this.cm.findColumnIndex(this.lastGroupField);\r
70351                 this.cm.setHidden(oldIndex, false);\r
70352                 this.lastGroupField = groupField;\r
70353                 this.cm.setHidden(colIndex, true);\r
70354             }\r
70355         }\r
70356         return Ext.grid.GroupingView.superclass.renderRows.apply(\r
70357                     this, arguments);\r
70358     },\r
70359 \r
70360     // private\r
70361     doRender : function(cs, rs, ds, startRow, colCount, stripe){\r
70362         if(rs.length < 1){\r
70363             return '';\r
70364         }\r
70365 \r
70366         if(!this.canGroup() || this.isUpdating){\r
70367             return Ext.grid.GroupingView.superclass.doRender.apply(\r
70368                     this, arguments);\r
70369         }\r
70370 \r
70371         var groupField = this.getGroupField(),\r
70372             colIndex = this.cm.findColumnIndex(groupField),\r
70373             g,\r
70374             gstyle = 'width:' + this.getTotalWidth() + ';',\r
70375             cfg = this.cm.config[colIndex],\r
70376             groupRenderer = cfg.groupRenderer || cfg.renderer,\r
70377             prefix = this.showGroupName ? (cfg.groupName || cfg.header)+': ' : '',\r
70378             groups = [],\r
70379             curGroup, i, len, gid;\r
70380 \r
70381         for(i = 0, len = rs.length; i < len; i++){\r
70382             var rowIndex = startRow + i,\r
70383                 r = rs[i],\r
70384                 gvalue = r.data[groupField];\r
70385 \r
70386                 g = this.getGroup(gvalue, r, groupRenderer, rowIndex, colIndex, ds);\r
70387             if(!curGroup || curGroup.group != g){\r
70388                 gid = this.constructId(gvalue, groupField, colIndex);\r
70389                 // if state is defined use it, however state is in terms of expanded\r
70390                 // so negate it, otherwise use the default.\r
70391                 this.state[gid] = !(Ext.isDefined(this.state[gid]) ? !this.state[gid] : this.startCollapsed);\r
70392                 curGroup = {\r
70393                     group: g,\r
70394                     gvalue: gvalue,\r
70395                     text: prefix + g,\r
70396                     groupId: gid,\r
70397                     startRow: rowIndex,\r
70398                     rs: [r],\r
70399                     cls: this.state[gid] ? '' : 'x-grid-group-collapsed',\r
70400                     style: gstyle\r
70401                 };\r
70402                 groups.push(curGroup);\r
70403             }else{\r
70404                 curGroup.rs.push(r);\r
70405             }\r
70406             r._groupId = gid;\r
70407         }\r
70408 \r
70409         var buf = [];\r
70410         for(i = 0, len = groups.length; i < len; i++){\r
70411             g = groups[i];\r
70412             this.doGroupStart(buf, g, cs, ds, colCount);\r
70413             buf[buf.length] = Ext.grid.GroupingView.superclass.doRender.call(\r
70414                     this, cs, g.rs, ds, g.startRow, colCount, stripe);\r
70415 \r
70416             this.doGroupEnd(buf, g, cs, ds, colCount);\r
70417         }\r
70418         return buf.join('');\r
70419     },\r
70420 \r
70421     /**\r
70422      * Dynamically tries to determine the groupId of a specific value\r
70423      * @param {String} value\r
70424      * @return {String} The group id\r
70425      */\r
70426     getGroupId : function(value){\r
70427         var field = this.getGroupField();\r
70428         return this.constructId(value, field, this.cm.findColumnIndex(field));\r
70429     },\r
70430 \r
70431     // private\r
70432     constructId : function(value, field, idx){\r
70433         var cfg = this.cm.config[idx],\r
70434             groupRenderer = cfg.groupRenderer || cfg.renderer,\r
70435             val = (this.groupMode == 'value') ? value : this.getGroup(value, {data:{}}, groupRenderer, 0, idx, this.ds);\r
70436 \r
70437         return this.getPrefix(field) + Ext.util.Format.htmlEncode(val);\r
70438     },\r
70439 \r
70440     // private\r
70441     canGroup  : function(){\r
70442         return this.enableGrouping && !!this.getGroupField();\r
70443     },\r
70444 \r
70445     // private\r
70446     getPrefix: function(field){\r
70447         return this.grid.getGridEl().id + '-gp-' + field + '-';\r
70448     },\r
70449 \r
70450     // private\r
70451     doGroupStart : function(buf, g, cs, ds, colCount){\r
70452         buf[buf.length] = this.startGroup.apply(g);\r
70453     },\r
70454 \r
70455     // private\r
70456     doGroupEnd : function(buf, g, cs, ds, colCount){\r
70457         buf[buf.length] = this.endGroup;\r
70458     },\r
70459 \r
70460     // private\r
70461     getRows : function(){\r
70462         if(!this.canGroup()){\r
70463             return Ext.grid.GroupingView.superclass.getRows.call(this);\r
70464         }\r
70465         var r = [];\r
70466         var g, gs = this.getGroups();\r
70467         for(var i = 0, len = gs.length; i < len; i++){\r
70468             g = gs[i].childNodes[1].childNodes;\r
70469             for(var j = 0, jlen = g.length; j < jlen; j++){\r
70470                 r[r.length] = g[j];\r
70471             }\r
70472         }\r
70473         return r;\r
70474     },\r
70475 \r
70476     // private\r
70477     updateGroupWidths : function(){\r
70478         if(!this.canGroup() || !this.hasRows()){\r
70479             return;\r
70480         }\r
70481         var tw = Math.max(this.cm.getTotalWidth(), this.el.dom.offsetWidth-this.getScrollOffset()) +'px';\r
70482         var gs = this.getGroups();\r
70483         for(var i = 0, len = gs.length; i < len; i++){\r
70484             gs[i].firstChild.style.width = tw;\r
70485         }\r
70486     },\r
70487 \r
70488     // private\r
70489     onColumnWidthUpdated : function(col, w, tw){\r
70490         Ext.grid.GroupingView.superclass.onColumnWidthUpdated.call(this, col, w, tw);\r
70491         this.updateGroupWidths();\r
70492     },\r
70493 \r
70494     // private\r
70495     onAllColumnWidthsUpdated : function(ws, tw){\r
70496         Ext.grid.GroupingView.superclass.onAllColumnWidthsUpdated.call(this, ws, tw);\r
70497         this.updateGroupWidths();\r
70498     },\r
70499 \r
70500     // private\r
70501     onColumnHiddenUpdated : function(col, hidden, tw){\r
70502         Ext.grid.GroupingView.superclass.onColumnHiddenUpdated.call(this, col, hidden, tw);\r
70503         this.updateGroupWidths();\r
70504     },\r
70505 \r
70506     // private\r
70507     onLayout : function(){\r
70508         this.updateGroupWidths();\r
70509     },\r
70510 \r
70511     // private\r
70512     onBeforeRowSelect : function(sm, rowIndex){\r
70513         this.toggleRowIndex(rowIndex, true);\r
70514     }\r
70515 });\r
70516 // private\r
70517 Ext.grid.GroupingView.GROUP_ID = 1000;