Upgrade to ExtJS 3.1.1 - Released 02/08/2010
[extjs.git] / pkgs / ext-foundation-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