Upgrade to ExtJS 3.1.0 - Released 12/16/2009
[extjs.git] / pkgs / ext-foundation-debug.js
1 /*!
2  * Ext JS Library 3.1.0
3  * Copyright(c) 2006-2009 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 </ul>\r
980 <h4>CSS Value Selectors:</h4>\r
981 <ul class="list">\r
982     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>\r
983     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>\r
984     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>\r
985     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>\r
986     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>\r
987     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>\r
988 </ul>\r
989  * @singleton\r
990  */\r
991 Ext.DomQuery = function(){\r
992     var cache = {}, \r
993         simpleCache = {}, \r
994         valueCache = {},\r
995         nonSpace = /\S/,\r
996         trimRe = /^\s+|\s+$/g,\r
997         tplRe = /\{(\d+)\}/g,\r
998         modeRe = /^(\s?[\/>+~]\s?|\s|$)/,\r
999         tagTokenRe = /^(#)?([\w-\*]+)/,\r
1000         nthRe = /(\d*)n\+?(\d*)/, \r
1001         nthRe2 = /\D/,\r
1002         // This is for IE MSXML which does not support expandos.\r
1003             // IE runs the same speed using setAttribute, however FF slows way down\r
1004             // and Safari completely fails so they need to continue to use expandos.\r
1005             isIE = window.ActiveXObject ? true : false,\r
1006             key = 30803;\r
1007             \r
1008     // this eval is stop the compressor from\r
1009         // renaming the variable to something shorter\r
1010         eval("var batch = 30803;");     \r
1011 \r
1012     function child(p, index){\r
1013         var i = 0,\r
1014                 n = p.firstChild;\r
1015         while(n){\r
1016             if(n.nodeType == 1){\r
1017                if(++i == index){\r
1018                    return n;\r
1019                }\r
1020             }\r
1021             n = n.nextSibling;\r
1022         }\r
1023         return null;\r
1024     };\r
1025 \r
1026     function next(n){\r
1027         while((n = n.nextSibling) && n.nodeType != 1);\r
1028         return n;\r
1029     };\r
1030 \r
1031     function prev(n){\r
1032         while((n = n.previousSibling) && n.nodeType != 1);\r
1033         return n;\r
1034     };\r
1035 \r
1036     function children(d){\r
1037         var n = d.firstChild, ni = -1,\r
1038                 nx;\r
1039             while(n){\r
1040                 nx = n.nextSibling;\r
1041                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){\r
1042                     d.removeChild(n);\r
1043                 }else{\r
1044                     n.nodeIndex = ++ni;\r
1045                 }\r
1046                 n = nx;\r
1047             }\r
1048             return this;\r
1049         };\r
1050 \r
1051     function byClassName(c, a, v){\r
1052         if(!v){\r
1053             return c;\r
1054         }\r
1055         var r = [], ri = -1, cn;\r
1056         for(var i = 0, ci; ci = c[i]; i++){\r
1057             if((' '+ci.className+' ').indexOf(v) != -1){\r
1058                 r[++ri] = ci;\r
1059             }\r
1060         }\r
1061         return r;\r
1062     };\r
1063 \r
1064     function attrValue(n, attr){\r
1065         if(!n.tagName && typeof n.length != "undefined"){\r
1066             n = n[0];\r
1067         }\r
1068         if(!n){\r
1069             return null;\r
1070         }\r
1071         if(attr == "for"){\r
1072             return n.htmlFor;\r
1073         }\r
1074         if(attr == "class" || attr == "className"){\r
1075             return n.className;\r
1076         }\r
1077         return n.getAttribute(attr) || n[attr];\r
1078 \r
1079     };\r
1080 \r
1081     function getNodes(ns, mode, tagName){\r
1082         var result = [], ri = -1, cs;\r
1083         if(!ns){\r
1084             return result;\r
1085         }\r
1086         tagName = tagName || "*";\r
1087         if(typeof ns.getElementsByTagName != "undefined"){\r
1088             ns = [ns];\r
1089         }\r
1090         if(!mode){\r
1091             for(var i = 0, ni; ni = ns[i]; i++){\r
1092                 cs = ni.getElementsByTagName(tagName);\r
1093                 for(var j = 0, ci; ci = cs[j]; j++){\r
1094                     result[++ri] = ci;\r
1095                 }\r
1096             }\r
1097         }else if(mode == "/" || mode == ">"){\r
1098             var utag = tagName.toUpperCase();\r
1099             for(var i = 0, ni, cn; ni = ns[i]; i++){\r
1100                 cn = ni.childNodes;\r
1101                 for(var j = 0, cj; cj = cn[j]; j++){\r
1102                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){\r
1103                         result[++ri] = cj;\r
1104                     }\r
1105                 }\r
1106             }\r
1107         }else if(mode == "+"){\r
1108             var utag = tagName.toUpperCase();\r
1109             for(var i = 0, n; n = ns[i]; i++){\r
1110                 while((n = n.nextSibling) && n.nodeType != 1);\r
1111                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){\r
1112                     result[++ri] = n;\r
1113                 }\r
1114             }\r
1115         }else if(mode == "~"){\r
1116             var utag = tagName.toUpperCase();\r
1117             for(var i = 0, n; n = ns[i]; i++){\r
1118                 while((n = n.nextSibling)){\r
1119                     if (n.nodeName == utag || n.nodeName == tagName || tagName == '*'){\r
1120                         result[++ri] = n;\r
1121                     }\r
1122                 }\r
1123             }\r
1124         }\r
1125         return result;\r
1126     };\r
1127 \r
1128     function concat(a, b){\r
1129         if(b.slice){\r
1130             return a.concat(b);\r
1131         }\r
1132         for(var i = 0, l = b.length; i < l; i++){\r
1133             a[a.length] = b[i];\r
1134         }\r
1135         return a;\r
1136     }\r
1137 \r
1138     function byTag(cs, tagName){\r
1139         if(cs.tagName || cs == document){\r
1140             cs = [cs];\r
1141         }\r
1142         if(!tagName){\r
1143             return cs;\r
1144         }\r
1145         var r = [], ri = -1;\r
1146         tagName = tagName.toLowerCase();\r
1147         for(var i = 0, ci; ci = cs[i]; i++){\r
1148             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){\r
1149                 r[++ri] = ci;\r
1150             }\r
1151         }\r
1152         return r;\r
1153     };\r
1154 \r
1155     function byId(cs, attr, id){\r
1156         if(cs.tagName || cs == document){\r
1157             cs = [cs];\r
1158         }\r
1159         if(!id){\r
1160             return cs;\r
1161         }\r
1162         var r = [], ri = -1;\r
1163         for(var i = 0,ci; ci = cs[i]; i++){\r
1164             if(ci && ci.id == id){\r
1165                 r[++ri] = ci;\r
1166                 return r;\r
1167             }\r
1168         }\r
1169         return r;\r
1170     };\r
1171 \r
1172     function byAttribute(cs, attr, value, op, custom){\r
1173         var r = [], \r
1174                 ri = -1, \r
1175                 st = custom=="{",\r
1176                 f = Ext.DomQuery.operators[op];\r
1177         for(var i = 0, ci; ci = cs[i]; i++){\r
1178             if(ci.nodeType != 1){\r
1179                 continue;\r
1180             }\r
1181             var a;\r
1182             if(st){\r
1183                 a = Ext.DomQuery.getStyle(ci, attr);\r
1184             }\r
1185             else if(attr == "class" || attr == "className"){\r
1186                 a = ci.className;\r
1187             }else if(attr == "for"){\r
1188                 a = ci.htmlFor;\r
1189             }else if(attr == "href"){\r
1190                 a = ci.getAttribute("href", 2);\r
1191             }else{\r
1192                 a = ci.getAttribute(attr);\r
1193             }\r
1194             if((f && f(a, value)) || (!f && a)){\r
1195                 r[++ri] = ci;\r
1196             }\r
1197         }\r
1198         return r;\r
1199     };\r
1200 \r
1201     function byPseudo(cs, name, value){\r
1202         return Ext.DomQuery.pseudos[name](cs, value);\r
1203     };\r
1204 \r
1205     function nodupIEXml(cs){\r
1206         var d = ++key, \r
1207                 r;\r
1208         cs[0].setAttribute("_nodup", d);\r
1209         r = [cs[0]];\r
1210         for(var i = 1, len = cs.length; i < len; i++){\r
1211             var c = cs[i];\r
1212             if(!c.getAttribute("_nodup") != d){\r
1213                 c.setAttribute("_nodup", d);\r
1214                 r[r.length] = c;\r
1215             }\r
1216         }\r
1217         for(var i = 0, len = cs.length; i < len; i++){\r
1218             cs[i].removeAttribute("_nodup");\r
1219         }\r
1220         return r;\r
1221     }\r
1222 \r
1223     function nodup(cs){\r
1224         if(!cs){\r
1225             return [];\r
1226         }\r
1227         var len = cs.length, c, i, r = cs, cj, ri = -1;\r
1228         if(!len || typeof cs.nodeType != "undefined" || len == 1){\r
1229             return cs;\r
1230         }\r
1231         if(isIE && typeof cs[0].selectSingleNode != "undefined"){\r
1232             return nodupIEXml(cs);\r
1233         }\r
1234         var d = ++key;\r
1235         cs[0]._nodup = d;\r
1236         for(i = 1; c = cs[i]; i++){\r
1237             if(c._nodup != d){\r
1238                 c._nodup = d;\r
1239             }else{\r
1240                 r = [];\r
1241                 for(var j = 0; j < i; j++){\r
1242                     r[++ri] = cs[j];\r
1243                 }\r
1244                 for(j = i+1; cj = cs[j]; j++){\r
1245                     if(cj._nodup != d){\r
1246                         cj._nodup = d;\r
1247                         r[++ri] = cj;\r
1248                     }\r
1249                 }\r
1250                 return r;\r
1251             }\r
1252         }\r
1253         return r;\r
1254     }\r
1255 \r
1256     function quickDiffIEXml(c1, c2){\r
1257         var d = ++key,\r
1258                 r = [];\r
1259         for(var i = 0, len = c1.length; i < len; i++){\r
1260             c1[i].setAttribute("_qdiff", d);\r
1261         }        \r
1262         for(var i = 0, len = c2.length; i < len; i++){\r
1263             if(c2[i].getAttribute("_qdiff") != d){\r
1264                 r[r.length] = c2[i];\r
1265             }\r
1266         }\r
1267         for(var i = 0, len = c1.length; i < len; i++){\r
1268            c1[i].removeAttribute("_qdiff");\r
1269         }\r
1270         return r;\r
1271     }\r
1272 \r
1273     function quickDiff(c1, c2){\r
1274         var len1 = c1.length,\r
1275                 d = ++key,\r
1276                 r = [];\r
1277         if(!len1){\r
1278             return c2;\r
1279         }\r
1280         if(isIE && typeof c1[0].selectSingleNode != "undefined"){\r
1281             return quickDiffIEXml(c1, c2);\r
1282         }        \r
1283         for(var i = 0; i < len1; i++){\r
1284             c1[i]._qdiff = d;\r
1285         }        \r
1286         for(var i = 0, len = c2.length; i < len; i++){\r
1287             if(c2[i]._qdiff != d){\r
1288                 r[r.length] = c2[i];\r
1289             }\r
1290         }\r
1291         return r;\r
1292     }\r
1293 \r
1294     function quickId(ns, mode, root, id){\r
1295         if(ns == root){\r
1296            var d = root.ownerDocument || root;\r
1297            return d.getElementById(id);\r
1298         }\r
1299         ns = getNodes(ns, mode, "*");\r
1300         return byId(ns, null, id);\r
1301     }\r
1302 \r
1303     return {\r
1304         getStyle : function(el, name){\r
1305             return Ext.fly(el).getStyle(name);\r
1306         },\r
1307         /**\r
1308          * Compiles a selector/xpath query into a reusable function. The returned function\r
1309          * takes one parameter "root" (optional), which is the context node from where the query should start.\r
1310          * @param {String} selector The selector/xpath query\r
1311          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match\r
1312          * @return {Function}\r
1313          */\r
1314         compile : function(path, type){\r
1315             type = type || "select";\r
1316 \r
1317             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"],\r
1318                 q = path, mode, lq,\r
1319                 tk = Ext.DomQuery.matchers,\r
1320                 tklen = tk.length,\r
1321                 mm,\r
1322                 // accept leading mode switch\r
1323                 lmode = q.match(modeRe);\r
1324             \r
1325             if(lmode && lmode[1]){\r
1326                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';\r
1327                 q = q.replace(lmode[1], "");\r
1328             }\r
1329             // strip leading slashes\r
1330             while(path.substr(0, 1)=="/"){\r
1331                 path = path.substr(1);\r
1332             }\r
1333 \r
1334             while(q && lq != q){\r
1335                 lq = q;\r
1336                 var tm = q.match(tagTokenRe);\r
1337                 if(type == "select"){\r
1338                     if(tm){\r
1339                         if(tm[1] == "#"){\r
1340                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';\r
1341                         }else{\r
1342                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';\r
1343                         }\r
1344                         q = q.replace(tm[0], "");\r
1345                     }else if(q.substr(0, 1) != '@'){\r
1346                         fn[fn.length] = 'n = getNodes(n, mode, "*");';\r
1347                     }\r
1348                 }else{\r
1349                     if(tm){\r
1350                         if(tm[1] == "#"){\r
1351                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';\r
1352                         }else{\r
1353                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';\r
1354                         }\r
1355                         q = q.replace(tm[0], "");\r
1356                     }\r
1357                 }\r
1358                 while(!(mm = q.match(modeRe))){\r
1359                     var matched = false;\r
1360                     for(var j = 0; j < tklen; j++){\r
1361                         var t = tk[j];\r
1362                         var m = q.match(t.re);\r
1363                         if(m){\r
1364                             fn[fn.length] = t.select.replace(tplRe, function(x, i){\r
1365                                                     return m[i];\r
1366                                                 });\r
1367                             q = q.replace(m[0], "");\r
1368                             matched = true;\r
1369                             break;\r
1370                         }\r
1371                     }\r
1372                     // prevent infinite loop on bad selector\r
1373                     if(!matched){\r
1374                         throw 'Error parsing selector, parsing failed at "' + q + '"';\r
1375                     }\r
1376                 }\r
1377                 if(mm[1]){\r
1378                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';\r
1379                     q = q.replace(mm[1], "");\r
1380                 }\r
1381             }\r
1382             fn[fn.length] = "return nodup(n);\n}";\r
1383             eval(fn.join(""));\r
1384             return f;\r
1385         },\r
1386 \r
1387         /**\r
1388          * Selects a group of elements.\r
1389          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)\r
1390          * @param {Node} root (optional) The start of the query (defaults to document).\r
1391          * @return {Array} An Array of DOM elements which match the selector. If there are\r
1392          * no matches, and empty Array is returned.\r
1393          */\r
1394         select : function(path, root, type){\r
1395             if(!root || root == document){\r
1396                 root = document;\r
1397             }\r
1398             if(typeof root == "string"){\r
1399                 root = document.getElementById(root);\r
1400             }\r
1401             var paths = path.split(","),\r
1402                 results = [];\r
1403             for(var i = 0, len = paths.length; i < len; i++){\r
1404                 var p = paths[i].replace(trimRe, "");\r
1405                 if(!cache[p]){\r
1406                     cache[p] = Ext.DomQuery.compile(p);\r
1407                     if(!cache[p]){\r
1408                         throw p + " is not a valid selector";\r
1409                     }\r
1410                 }\r
1411                 var result = cache[p](root);\r
1412                 if(result && result != document){\r
1413                     results = results.concat(result);\r
1414                 }\r
1415             }\r
1416             if(paths.length > 1){\r
1417                 return nodup(results);\r
1418             }\r
1419             return results;\r
1420         },\r
1421 \r
1422         /**\r
1423          * Selects a single element.\r
1424          * @param {String} selector The selector/xpath query\r
1425          * @param {Node} root (optional) The start of the query (defaults to document).\r
1426          * @return {Element} The DOM element which matched the selector.\r
1427          */\r
1428         selectNode : function(path, root){\r
1429             return Ext.DomQuery.select(path, root)[0];\r
1430         },\r
1431 \r
1432         /**\r
1433          * Selects the value of a node, optionally replacing null with the defaultValue.\r
1434          * @param {String} selector The selector/xpath query\r
1435          * @param {Node} root (optional) The start of the query (defaults to document).\r
1436          * @param {String} defaultValue\r
1437          * @return {String}\r
1438          */\r
1439         selectValue : function(path, root, defaultValue){\r
1440             path = path.replace(trimRe, "");\r
1441             if(!valueCache[path]){\r
1442                 valueCache[path] = Ext.DomQuery.compile(path, "select");\r
1443             }\r
1444             var n = valueCache[path](root), v;\r
1445             n = n[0] ? n[0] : n;\r
1446             \r
1447             if (typeof n.normalize == 'function') n.normalize();\r
1448             \r
1449             v = (n && n.firstChild ? n.firstChild.nodeValue : null);\r
1450             return ((v === null||v === undefined||v==='') ? defaultValue : v);\r
1451         },\r
1452 \r
1453         /**\r
1454          * Selects the value of a node, parsing integers and floats. Returns the defaultValue, or 0 if none is specified.\r
1455          * @param {String} selector The selector/xpath query\r
1456          * @param {Node} root (optional) The start of the query (defaults to document).\r
1457          * @param {Number} defaultValue\r
1458          * @return {Number}\r
1459          */\r
1460         selectNumber : function(path, root, defaultValue){\r
1461             var v = Ext.DomQuery.selectValue(path, root, defaultValue || 0);\r
1462             return parseFloat(v);\r
1463         },\r
1464 \r
1465         /**\r
1466          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)\r
1467          * @param {String/HTMLElement/Array} el An element id, element or array of elements\r
1468          * @param {String} selector The simple selector to test\r
1469          * @return {Boolean}\r
1470          */\r
1471         is : function(el, ss){\r
1472             if(typeof el == "string"){\r
1473                 el = document.getElementById(el);\r
1474             }\r
1475             var isArray = Ext.isArray(el),\r
1476                 result = Ext.DomQuery.filter(isArray ? el : [el], ss);\r
1477             return isArray ? (result.length == el.length) : (result.length > 0);\r
1478         },\r
1479 \r
1480         /**\r
1481          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)\r
1482          * @param {Array} el An array of elements to filter\r
1483          * @param {String} selector The simple selector to test\r
1484          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match\r
1485          * the selector instead of the ones that match\r
1486          * @return {Array} An Array of DOM elements which match the selector. If there are\r
1487          * no matches, and empty Array is returned.\r
1488          */\r
1489         filter : function(els, ss, nonMatches){\r
1490             ss = ss.replace(trimRe, "");\r
1491             if(!simpleCache[ss]){\r
1492                 simpleCache[ss] = Ext.DomQuery.compile(ss, "simple");\r
1493             }\r
1494             var result = simpleCache[ss](els);\r
1495             return nonMatches ? quickDiff(result, els) : result;\r
1496         },\r
1497 \r
1498         /**\r
1499          * Collection of matching regular expressions and code snippets.\r
1500          */\r
1501         matchers : [{\r
1502                 re: /^\.([\w-]+)/,\r
1503                 select: 'n = byClassName(n, null, " {1} ");'\r
1504             }, {\r
1505                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,\r
1506                 select: 'n = byPseudo(n, "{1}", "{2}");'\r
1507             },{\r
1508                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,\r
1509                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'\r
1510             }, {\r
1511                 re: /^#([\w-]+)/,\r
1512                 select: 'n = byId(n, null, "{1}");'\r
1513             },{\r
1514                 re: /^@([\w-]+)/,\r
1515                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'\r
1516             }\r
1517         ],\r
1518 \r
1519         /**\r
1520          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.\r
1521          * 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
1522          */\r
1523         operators : {\r
1524             "=" : function(a, v){\r
1525                 return a == v;\r
1526             },\r
1527             "!=" : function(a, v){\r
1528                 return a != v;\r
1529             },\r
1530             "^=" : function(a, v){\r
1531                 return a && a.substr(0, v.length) == v;\r
1532             },\r
1533             "$=" : function(a, v){\r
1534                 return a && a.substr(a.length-v.length) == v;\r
1535             },\r
1536             "*=" : function(a, v){\r
1537                 return a && a.indexOf(v) !== -1;\r
1538             },\r
1539             "%=" : function(a, v){\r
1540                 return (a % v) == 0;\r
1541             },\r
1542             "|=" : function(a, v){\r
1543                 return a && (a == v || a.substr(0, v.length+1) == v+'-');\r
1544             },\r
1545             "~=" : function(a, v){\r
1546                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;\r
1547             }\r
1548         },\r
1549 \r
1550         /**\r
1551          * <p>Object hash of "pseudo class" filter functions which are used when filtering selections. Each function is passed\r
1552          * two parameters:</p><div class="mdetail-params"><ul>\r
1553          * <li><b>c</b> : Array<div class="sub-desc">An Array of DOM elements to filter.</div></li>\r
1554          * <li><b>v</b> : String<div class="sub-desc">The argument (if any) supplied in the selector.</div></li>\r
1555          * </ul></div>\r
1556          * <p>A filter function returns an Array of DOM elements which conform to the pseudo class.</p>\r
1557          * <p>In addition to the provided pseudo classes listed above such as <code>first-child</code> and <code>nth-child</code>,\r
1558          * developers may add additional, custom psuedo class filters to select elements according to application-specific requirements.</p>\r
1559          * <p>For example, to filter <code>&lt;a></code> elements to only return links to <i>external</i> resources:</p>\r
1560          * <code><pre>\r
1561 Ext.DomQuery.pseudos.external = function(c, v){\r
1562     var r = [], ri = -1;\r
1563     for(var i = 0, ci; ci = c[i]; i++){\r
1564 //      Include in result set only if it's a link to an external resource\r
1565         if(ci.hostname != location.hostname){\r
1566             r[++ri] = ci;\r
1567         }\r
1568     }\r
1569     return r;\r
1570 };</pre></code>\r
1571          * Then external links could be gathered with the following statement:<code><pre>\r
1572 var externalLinks = Ext.select("a:external");\r
1573 </code></pre>\r
1574          */\r
1575         pseudos : {\r
1576             "first-child" : function(c){\r
1577                 var r = [], ri = -1, n;\r
1578                 for(var i = 0, ci; ci = n = c[i]; i++){\r
1579                     while((n = n.previousSibling) && n.nodeType != 1);\r
1580                     if(!n){\r
1581                         r[++ri] = ci;\r
1582                     }\r
1583                 }\r
1584                 return r;\r
1585             },\r
1586 \r
1587             "last-child" : function(c){\r
1588                 var r = [], ri = -1, n;\r
1589                 for(var i = 0, ci; ci = n = c[i]; i++){\r
1590                     while((n = n.nextSibling) && n.nodeType != 1);\r
1591                     if(!n){\r
1592                         r[++ri] = ci;\r
1593                     }\r
1594                 }\r
1595                 return r;\r
1596             },\r
1597 \r
1598             "nth-child" : function(c, a) {\r
1599                 var r = [], ri = -1,\r
1600                         m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a),\r
1601                         f = (m[1] || 1) - 0, l = m[2] - 0;\r
1602                 for(var i = 0, n; n = c[i]; i++){\r
1603                     var pn = n.parentNode;\r
1604                     if (batch != pn._batch) {\r
1605                         var j = 0;\r
1606                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){\r
1607                             if(cn.nodeType == 1){\r
1608                                cn.nodeIndex = ++j;\r
1609                             }\r
1610                         }\r
1611                         pn._batch = batch;\r
1612                     }\r
1613                     if (f == 1) {\r
1614                         if (l == 0 || n.nodeIndex == l){\r
1615                             r[++ri] = n;\r
1616                         }\r
1617                     } else if ((n.nodeIndex + l) % f == 0){\r
1618                         r[++ri] = n;\r
1619                     }\r
1620                 }\r
1621 \r
1622                 return r;\r
1623             },\r
1624 \r
1625             "only-child" : function(c){\r
1626                 var r = [], ri = -1;;\r
1627                 for(var i = 0, ci; ci = c[i]; i++){\r
1628                     if(!prev(ci) && !next(ci)){\r
1629                         r[++ri] = ci;\r
1630                     }\r
1631                 }\r
1632                 return r;\r
1633             },\r
1634 \r
1635             "empty" : function(c){\r
1636                 var r = [], ri = -1;\r
1637                 for(var i = 0, ci; ci = c[i]; i++){\r
1638                     var cns = ci.childNodes, j = 0, cn, empty = true;\r
1639                     while(cn = cns[j]){\r
1640                         ++j;\r
1641                         if(cn.nodeType == 1 || cn.nodeType == 3){\r
1642                             empty = false;\r
1643                             break;\r
1644                         }\r
1645                     }\r
1646                     if(empty){\r
1647                         r[++ri] = ci;\r
1648                     }\r
1649                 }\r
1650                 return r;\r
1651             },\r
1652 \r
1653             "contains" : function(c, v){\r
1654                 var r = [], ri = -1;\r
1655                 for(var i = 0, ci; ci = c[i]; i++){\r
1656                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){\r
1657                         r[++ri] = ci;\r
1658                     }\r
1659                 }\r
1660                 return r;\r
1661             },\r
1662 \r
1663             "nodeValue" : function(c, v){\r
1664                 var r = [], ri = -1;\r
1665                 for(var i = 0, ci; ci = c[i]; i++){\r
1666                     if(ci.firstChild && ci.firstChild.nodeValue == v){\r
1667                         r[++ri] = ci;\r
1668                     }\r
1669                 }\r
1670                 return r;\r
1671             },\r
1672 \r
1673             "checked" : function(c){\r
1674                 var r = [], ri = -1;\r
1675                 for(var i = 0, ci; ci = c[i]; i++){\r
1676                     if(ci.checked == true){\r
1677                         r[++ri] = ci;\r
1678                     }\r
1679                 }\r
1680                 return r;\r
1681             },\r
1682 \r
1683             "not" : function(c, ss){\r
1684                 return Ext.DomQuery.filter(c, ss, true);\r
1685             },\r
1686 \r
1687             "any" : function(c, selectors){\r
1688                 var ss = selectors.split('|'),\r
1689                         r = [], ri = -1, s;\r
1690                 for(var i = 0, ci; ci = c[i]; i++){\r
1691                     for(var j = 0; s = ss[j]; j++){\r
1692                         if(Ext.DomQuery.is(ci, s)){\r
1693                             r[++ri] = ci;\r
1694                             break;\r
1695                         }\r
1696                     }\r
1697                 }\r
1698                 return r;\r
1699             },\r
1700 \r
1701             "odd" : function(c){\r
1702                 return this["nth-child"](c, "odd");\r
1703             },\r
1704 \r
1705             "even" : function(c){\r
1706                 return this["nth-child"](c, "even");\r
1707             },\r
1708 \r
1709             "nth" : function(c, a){\r
1710                 return c[a-1] || [];\r
1711             },\r
1712 \r
1713             "first" : function(c){\r
1714                 return c[0] || [];\r
1715             },\r
1716 \r
1717             "last" : function(c){\r
1718                 return c[c.length-1] || [];\r
1719             },\r
1720 \r
1721             "has" : function(c, ss){\r
1722                 var s = Ext.DomQuery.select,\r
1723                         r = [], ri = -1;\r
1724                 for(var i = 0, ci; ci = c[i]; i++){\r
1725                     if(s(ss, ci).length > 0){\r
1726                         r[++ri] = ci;\r
1727                     }\r
1728                 }\r
1729                 return r;\r
1730             },\r
1731 \r
1732             "next" : function(c, ss){\r
1733                 var is = Ext.DomQuery.is,\r
1734                         r = [], ri = -1;\r
1735                 for(var i = 0, ci; ci = c[i]; i++){\r
1736                     var n = next(ci);\r
1737                     if(n && is(n, ss)){\r
1738                         r[++ri] = ci;\r
1739                     }\r
1740                 }\r
1741                 return r;\r
1742             },\r
1743 \r
1744             "prev" : function(c, ss){\r
1745                 var is = Ext.DomQuery.is,\r
1746                         r = [], ri = -1;\r
1747                 for(var i = 0, ci; ci = c[i]; i++){\r
1748                     var n = prev(ci);\r
1749                     if(n && is(n, ss)){\r
1750                         r[++ri] = ci;\r
1751                     }\r
1752                 }\r
1753                 return r;\r
1754             }\r
1755         }\r
1756     };\r
1757 }();\r
1758 \r
1759 /**\r
1760  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Ext.DomQuery#select}\r
1761  * @param {String} path The selector/xpath query\r
1762  * @param {Node} root (optional) The start of the query (defaults to document).\r
1763  * @return {Array}\r
1764  * @member Ext\r
1765  * @method query\r
1766  */\r
1767 Ext.query = Ext.DomQuery.select;\r
1768 /**
1769  * @class Ext.util.DelayedTask
1770  * <p> The DelayedTask class provides a convenient way to "buffer" the execution of a method,
1771  * performing setTimeout where a new timeout cancels the old timeout. When called, the
1772  * task will wait the specified time period before executing. If durng that time period,
1773  * the task is called again, the original call will be cancelled. This continues so that
1774  * the function is only called a single time for each iteration.</p>
1775  * <p>This method is especially useful for things like detecting whether a user has finished
1776  * typing in a text field. An example would be performing validation on a keypress. You can
1777  * use this class to buffer the keypress events for a certain number of milliseconds, and
1778  * perform only if they stop for that amount of time.  Usage:</p><pre><code>
1779 var task = new Ext.util.DelayedTask(function(){
1780     alert(Ext.getDom('myInputField').value.length);
1781 });
1782 // Wait 500ms before calling our function. If the user presses another key 
1783 // during that 500ms, it will be cancelled and we'll wait another 500ms.
1784 Ext.get('myInputField').on('keypress', function(){
1785     task.{@link #delay}(500); 
1786 });
1787  * </code></pre> 
1788  * <p>Note that we are using a DelayedTask here to illustrate a point. The configuration
1789  * option <tt>buffer</tt> for {@link Ext.util.Observable#addListener addListener/on} will
1790  * also setup a delayed task for you to buffer events.</p> 
1791  * @constructor The parameters to this constructor serve as defaults and are not required.
1792  * @param {Function} fn (optional) The default function to call.
1793  * @param {Object} scope The default scope (The <code><b>this</b></code> reference) in which the
1794  * function is called. If not specified, <code>this</code> will refer to the browser window.
1795  * @param {Array} args (optional) The default Array of arguments.
1796  */
1797 Ext.util.DelayedTask = function(fn, scope, args){
1798     var me = this,
1799         id,     
1800         call = function(){
1801                 clearInterval(id);
1802                 id = null;
1803                 fn.apply(scope, args || []);
1804             };
1805             
1806     /**
1807      * Cancels any pending timeout and queues a new one
1808      * @param {Number} delay The milliseconds to delay
1809      * @param {Function} newFn (optional) Overrides function passed to constructor
1810      * @param {Object} newScope (optional) Overrides scope passed to constructor. Remember that if no scope
1811      * is specified, <code>this</code> will refer to the browser window.
1812      * @param {Array} newArgs (optional) Overrides args passed to constructor
1813      */
1814     me.delay = function(delay, newFn, newScope, newArgs){
1815         me.cancel();
1816         fn = newFn || fn;
1817         scope = newScope || scope;
1818         args = newArgs || args;
1819         id = setInterval(call, delay);
1820     };
1821
1822     /**
1823      * Cancel the last queued timeout
1824      */
1825     me.cancel = function(){
1826         if(id){
1827             clearInterval(id);
1828             id = null;
1829         }
1830     };
1831 };(function(){
1832
1833 var EXTUTIL = Ext.util,
1834     TOARRAY = Ext.toArray,
1835     EACH = Ext.each,
1836     ISOBJECT = Ext.isObject,
1837     TRUE = true,
1838     FALSE = false;
1839 /**
1840  * @class Ext.util.Observable
1841  * Base class that provides a common interface for publishing events. Subclasses are expected to
1842  * to have a property "events" with all the events defined, and, optionally, a property "listeners"
1843  * with configured listeners defined.<br>
1844  * For example:
1845  * <pre><code>
1846 Employee = Ext.extend(Ext.util.Observable, {
1847     constructor: function(config){
1848         this.name = config.name;
1849         this.addEvents({
1850             "fired" : true,
1851             "quit" : true
1852         });
1853
1854         // Copy configured listeners into *this* object so that the base class&#39;s
1855         // constructor will add them.
1856         this.listeners = config.listeners;
1857
1858         // Call our superclass constructor to complete construction process.
1859         Employee.superclass.constructor.call(config)
1860     }
1861 });
1862 </code></pre>
1863  * This could then be used like this:<pre><code>
1864 var newEmployee = new Employee({
1865     name: employeeName,
1866     listeners: {
1867         quit: function() {
1868             // By default, "this" will be the object that fired the event.
1869             alert(this.name + " has quit!");
1870         }
1871     }
1872 });
1873 </code></pre>
1874  */
1875 EXTUTIL.Observable = function(){
1876     /**
1877      * @cfg {Object} listeners (optional) <p>A config object containing one or more event handlers to be added to this
1878      * object during initialization.  This should be a valid listeners config object as specified in the
1879      * {@link #addListener} example for attaching multiple handlers at once.</p>
1880      * <br><p><b><u>DOM events from ExtJs {@link Ext.Component Components}</u></b></p>
1881      * <br><p>While <i>some</i> ExtJs Component classes export selected DOM events (e.g. "click", "mouseover" etc), this
1882      * is usually only done when extra value can be added. For example the {@link Ext.DataView DataView}'s
1883      * <b><code>{@link Ext.DataView#click click}</code></b> event passing the node clicked on. To access DOM
1884      * events directly from a Component's HTMLElement, listeners must be added to the <i>{@link Ext.Component#getEl Element}</i> after the Component
1885      * has been rendered. A plugin can simplify this step:<pre><code>
1886 // Plugin is configured with a listeners config object.
1887 // The Component is appended to the argument list of all handler functions.
1888 Ext.DomObserver = Ext.extend(Object, {
1889     constructor: function(config) {
1890         this.listeners = config.listeners ? config.listeners : config;
1891     },
1892
1893     // Component passes itself into plugin&#39;s init method
1894     init: function(c) {
1895         var p, l = this.listeners;
1896         for (p in l) {
1897             if (Ext.isFunction(l[p])) {
1898                 l[p] = this.createHandler(l[p], c);
1899             } else {
1900                 l[p].fn = this.createHandler(l[p].fn, c);
1901             }
1902         }
1903
1904         // Add the listeners to the Element immediately following the render call
1905         c.render = c.render.{@link Function#createSequence createSequence}(function() {
1906             var e = c.getEl();
1907             if (e) {
1908                 e.on(l);
1909             }
1910         });
1911     },
1912
1913     createHandler: function(fn, c) {
1914         return function(e) {
1915             fn.call(this, e, c);
1916         };
1917     }
1918 });
1919
1920 var combo = new Ext.form.ComboBox({
1921
1922     // Collapse combo when its element is clicked on
1923     plugins: [ new Ext.DomObserver({
1924         click: function(evt, comp) {
1925             comp.collapse();
1926         }
1927     })],
1928     store: myStore,
1929     typeAhead: true,
1930     mode: 'local',
1931     triggerAction: 'all'
1932 });
1933      * </code></pre></p>
1934      */
1935     var me = this, e = me.events;
1936     if(me.listeners){
1937         me.on(me.listeners);
1938         delete me.listeners;
1939     }
1940     me.events = e || {};
1941 };
1942
1943 EXTUTIL.Observable.prototype = {
1944     // private
1945     filterOptRe : /^(?:scope|delay|buffer|single)$/,
1946
1947     /**
1948      * <p>Fires the specified event with the passed parameters (minus the event name).</p>
1949      * <p>An event may be set to bubble up an Observable parent hierarchy (See {@link Ext.Component#getBubbleTarget})
1950      * by calling {@link #enableBubble}.</p>
1951      * @param {String} eventName The name of the event to fire.
1952      * @param {Object...} args Variable number of parameters are passed to handlers.
1953      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true.
1954      */
1955     fireEvent : function(){
1956         var a = TOARRAY(arguments),
1957             ename = a[0].toLowerCase(),
1958             me = this,
1959             ret = TRUE,
1960             ce = me.events[ename],
1961             q,
1962             c;
1963         if (me.eventsSuspended === TRUE) {
1964             if (q = me.eventQueue) {
1965                 q.push(a);
1966             }
1967         }
1968         else if(ISOBJECT(ce) && ce.bubble){
1969             if(ce.fire.apply(ce, a.slice(1)) === FALSE) {
1970                 return FALSE;
1971             }
1972             c = me.getBubbleTarget && me.getBubbleTarget();
1973             if(c && c.enableBubble) {
1974                 if(!c.events[ename] || !Ext.isObject(c.events[ename]) || !c.events[ename].bubble) {
1975                     c.enableBubble(ename);
1976                 }
1977                 return c.fireEvent.apply(c, a);
1978             }
1979         }
1980         else {
1981             if (ISOBJECT(ce)) {
1982                 a.shift();
1983                 ret = ce.fire.apply(ce, a);
1984             }
1985         }
1986         return ret;
1987     },
1988
1989     /**
1990      * Appends an event handler to this object.
1991      * @param {String}   eventName The name of the event to listen for.
1992      * @param {Function} handler The method the event invokes.
1993      * @param {Object}   scope (optional) The scope (<code><b>this</b></code> reference) in which the handler function is executed.
1994      * <b>If omitted, defaults to the object which fired the event.</b>
1995      * @param {Object}   options (optional) An object containing handler configuration.
1996      * properties. This may contain any of the following properties:<ul>
1997      * <li><b>scope</b> : Object<div class="sub-desc">The scope (<code><b>this</b></code> reference) in which the handler function is executed.
1998      * <b>If omitted, defaults to the object which fired the event.</b></div></li>
1999      * <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>
2000      * <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>
2001      * <li><b>buffer</b> : Number<div class="sub-desc">Causes the handler to be scheduled to run in an {@link Ext.util.DelayedTask} delayed
2002      * by the specified number of milliseconds. If the event fires again within that time, the original
2003      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</div></li>
2004      * <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>
2005      * if the event was bubbled up from a child Observable.</div></li>
2006      * </ul><br>
2007      * <p>
2008      * <b>Combining Options</b><br>
2009      * Using the options argument, it is possible to combine different types of listeners:<br>
2010      * <br>
2011      * A delayed, one-time listener.
2012      * <pre><code>
2013 myDataView.on('click', this.onClick, this, {
2014 single: true,
2015 delay: 100
2016 });</code></pre>
2017      * <p>
2018      * <b>Attaching multiple handlers in 1 call</b><br>
2019      * The method also allows for a single argument to be passed which is a config object containing properties
2020      * which specify multiple handlers.
2021      * <p>
2022      * <pre><code>
2023 myGridPanel.on({
2024 'click' : {
2025     fn: this.onClick,
2026     scope: this,
2027     delay: 100
2028 },
2029 'mouseover' : {
2030     fn: this.onMouseOver,
2031     scope: this
2032 },
2033 'mouseout' : {
2034     fn: this.onMouseOut,
2035     scope: this
2036 }
2037 });</code></pre>
2038  * <p>
2039  * Or a shorthand syntax:<br>
2040  * <pre><code>
2041 myGridPanel.on({
2042 'click' : this.onClick,
2043 'mouseover' : this.onMouseOver,
2044 'mouseout' : this.onMouseOut,
2045  scope: this
2046 });</code></pre>
2047      */
2048     addListener : function(eventName, fn, scope, o){
2049         var me = this,
2050             e,
2051             oe,
2052             isF,
2053         ce;
2054         if (ISOBJECT(eventName)) {
2055             o = eventName;
2056             for (e in o){
2057                 oe = o[e];
2058                 if (!me.filterOptRe.test(e)) {
2059                     me.addListener(e, oe.fn || oe, oe.scope || o.scope, oe.fn ? oe : o);
2060                 }
2061             }
2062         } else {
2063             eventName = eventName.toLowerCase();
2064             ce = me.events[eventName] || TRUE;
2065             if (Ext.isBoolean(ce)) {
2066                 me.events[eventName] = ce = new EXTUTIL.Event(me, eventName);
2067             }
2068             ce.addListener(fn, scope, ISOBJECT(o) ? o : {});
2069         }
2070     },
2071
2072     /**
2073      * Removes an event handler.
2074      * @param {String}   eventName The type of event the handler was associated with.
2075      * @param {Function} handler   The handler to remove. <b>This must be a reference to the function passed into the {@link #addListener} call.</b>
2076      * @param {Object}   scope     (optional) The scope originally specified for the handler.
2077      */
2078     removeListener : function(eventName, fn, scope){
2079         var ce = this.events[eventName.toLowerCase()];
2080         if (ISOBJECT(ce)) {
2081             ce.removeListener(fn, scope);
2082         }
2083     },
2084
2085     /**
2086      * Removes all listeners for this object
2087      */
2088     purgeListeners : function(){
2089         var events = this.events,
2090             evt,
2091             key;
2092         for(key in events){
2093             evt = events[key];
2094             if(ISOBJECT(evt)){
2095                 evt.clearListeners();
2096             }
2097         }
2098     },
2099
2100     /**
2101      * Adds the specified events to the list of events which this Observable may fire.
2102      * @param {Object|String} o Either an object with event names as properties with a value of <code>true</code>
2103      * or the first event name string if multiple event names are being passed as separate parameters.
2104      * @param {string} Optional. Event name if multiple event names are being passed as separate parameters.
2105      * Usage:<pre><code>
2106 this.addEvents('storeloaded', 'storecleared');
2107 </code></pre>
2108      */
2109     addEvents : function(o){
2110         var me = this;
2111         me.events = me.events || {};
2112         if (Ext.isString(o)) {
2113             var a = arguments,
2114                 i = a.length;
2115             while(i--) {
2116                 me.events[a[i]] = me.events[a[i]] || TRUE;
2117             }
2118         } else {
2119             Ext.applyIf(me.events, o);
2120         }
2121     },
2122
2123     /**
2124      * Checks to see if this object has any listeners for a specified event
2125      * @param {String} eventName The name of the event to check for
2126      * @return {Boolean} True if the event is being listened for, else false
2127      */
2128     hasListener : function(eventName){
2129         var e = this.events[eventName];
2130         return ISOBJECT(e) && e.listeners.length > 0;
2131     },
2132
2133     /**
2134      * Suspend the firing of all events. (see {@link #resumeEvents})
2135      * @param {Boolean} queueSuspended Pass as true to queue up suspended events to be fired
2136      * after the {@link #resumeEvents} call instead of discarding all suspended events;
2137      */
2138     suspendEvents : function(queueSuspended){
2139         this.eventsSuspended = TRUE;
2140         if(queueSuspended && !this.eventQueue){
2141             this.eventQueue = [];
2142         }
2143     },
2144
2145     /**
2146      * Resume firing events. (see {@link #suspendEvents})
2147      * If events were suspended using the <tt><b>queueSuspended</b></tt> parameter, then all
2148      * events fired during event suspension will be sent to any listeners now.
2149      */
2150     resumeEvents : function(){
2151         var me = this,
2152             queued = me.eventQueue || [];
2153         me.eventsSuspended = FALSE;
2154         delete me.eventQueue;
2155         EACH(queued, function(e) {
2156             me.fireEvent.apply(me, e);
2157         });
2158     }
2159 };
2160
2161 var OBSERVABLE = EXTUTIL.Observable.prototype;
2162 /**
2163  * Appends an event handler to this object (shorthand for {@link #addListener}.)
2164  * @param {String}   eventName     The type of event to listen for
2165  * @param {Function} handler       The method the event invokes
2166  * @param {Object}   scope         (optional) The scope (<code><b>this</b></code> reference) in which the handler function is executed.
2167  * <b>If omitted, defaults to the object which fired the event.</b>
2168  * @param {Object}   options       (optional) An object containing handler configuration.
2169  * @method
2170  */
2171 OBSERVABLE.on = OBSERVABLE.addListener;
2172 /**
2173  * Removes an event handler (shorthand for {@link #removeListener}.)
2174  * @param {String}   eventName     The type of event the handler was associated with.
2175  * @param {Function} handler       The handler to remove. <b>This must be a reference to the function passed into the {@link #addListener} call.</b>
2176  * @param {Object}   scope         (optional) The scope originally specified for the handler.
2177  * @method
2178  */
2179 OBSERVABLE.un = OBSERVABLE.removeListener;
2180
2181 /**
2182  * Removes <b>all</b> added captures from the Observable.
2183  * @param {Observable} o The Observable to release
2184  * @static
2185  */
2186 EXTUTIL.Observable.releaseCapture = function(o){
2187     o.fireEvent = OBSERVABLE.fireEvent;
2188 };
2189
2190 function createTargeted(h, o, scope){
2191     return function(){
2192         if(o.target == arguments[0]){
2193             h.apply(scope, TOARRAY(arguments));
2194         }
2195     };
2196 };
2197
2198 function createBuffered(h, o, fn, scope){
2199     fn.task = new EXTUTIL.DelayedTask();
2200     return function(){
2201         fn.task.delay(o.buffer, h, scope, TOARRAY(arguments));
2202     };
2203 }
2204
2205 function createSingle(h, e, fn, scope){
2206     return function(){
2207         e.removeListener(fn, scope);
2208         return h.apply(scope, arguments);
2209     };
2210 }
2211
2212 function createDelayed(h, o, fn, scope){
2213     return function(){
2214         var task = new EXTUTIL.DelayedTask();
2215         if(!fn.tasks) {
2216             fn.tasks = [];
2217         }
2218         fn.tasks.push(task);
2219         task.delay(o.delay || 10, h, scope, TOARRAY(arguments));
2220     };
2221 };
2222
2223 EXTUTIL.Event = function(obj, name){
2224     this.name = name;
2225     this.obj = obj;
2226     this.listeners = [];
2227 };
2228
2229 EXTUTIL.Event.prototype = {
2230     addListener : function(fn, scope, options){
2231         var me = this,
2232             l;
2233         scope = scope || me.obj;
2234         if(!me.isListening(fn, scope)){
2235             l = me.createListener(fn, scope, options);
2236             if(me.firing){ // if we are currently firing this event, don't disturb the listener loop
2237                 me.listeners = me.listeners.slice(0);
2238             }
2239             me.listeners.push(l);
2240         }
2241     },
2242
2243     createListener: function(fn, scope, o){
2244         o = o || {}, scope = scope || this.obj;
2245         var l = {
2246             fn: fn,
2247             scope: scope,
2248             options: o
2249         }, h = fn;
2250         if(o.target){
2251             h = createTargeted(h, o, scope);
2252         }
2253         if(o.delay){
2254             h = createDelayed(h, o, fn, scope);
2255         }
2256         if(o.single){
2257             h = createSingle(h, this, fn, scope);
2258         }
2259         if(o.buffer){
2260             h = createBuffered(h, o, fn, scope);
2261         }
2262         l.fireFn = h;
2263         return l;
2264     },
2265
2266     findListener : function(fn, scope){
2267         var list = this.listeners,
2268             i = list.length,
2269             l,
2270             s;
2271         while(i--) {
2272             l = list[i];
2273             if(l) {
2274                 s = l.scope;
2275                 if(l.fn == fn && (s == scope || s == this.obj)){
2276                     return i;
2277                 }
2278             }
2279         }
2280         return -1;
2281     },
2282
2283     isListening : function(fn, scope){
2284         return this.findListener(fn, scope) != -1;
2285     },
2286
2287     removeListener : function(fn, scope){
2288         var index,
2289             l,
2290             k,
2291             me = this,
2292             ret = FALSE;
2293         if((index = me.findListener(fn, scope)) != -1){
2294             if (me.firing) {
2295                 me.listeners = me.listeners.slice(0);
2296             }
2297             l = me.listeners[index].fn;
2298             // Cancel buffered tasks
2299             if(l.task) {
2300                 l.task.cancel();
2301                 delete l.task;
2302             }
2303             // Cancel delayed tasks
2304             k = l.tasks && l.tasks.length;
2305             if(k) {
2306                 while(k--) {
2307                     l.tasks[k].cancel();
2308                 }
2309                 delete l.tasks;
2310             }
2311             me.listeners.splice(index, 1);
2312             ret = TRUE;
2313         }
2314         return ret;
2315     },
2316
2317     // Iterate to stop any buffered/delayed events
2318     clearListeners : function(){
2319         var me = this,
2320             l = me.listeners,
2321             i = l.length;
2322         while(i--) {
2323             me.removeListener(l[i].fn, l[i].scope);
2324         }
2325     },
2326
2327     fire : function(){
2328         var me = this,
2329             args = TOARRAY(arguments),
2330             listeners = me.listeners,
2331             len = listeners.length,
2332             i = 0,
2333             l;
2334
2335         if(len > 0){
2336             me.firing = TRUE;
2337             for (; i < len; i++) {
2338                 l = listeners[i];
2339                 if(l && l.fireFn.apply(l.scope || me.obj || window, args) === FALSE) {
2340                     return (me.firing = FALSE);
2341                 }
2342             }
2343         }
2344         me.firing = FALSE;
2345         return TRUE;
2346     }
2347 };
2348 })();/**\r
2349  * @class Ext.util.Observable\r
2350  */\r
2351 Ext.apply(Ext.util.Observable.prototype, function(){\r
2352     // this is considered experimental (along with beforeMethod, afterMethod, removeMethodListener?)\r
2353     // allows for easier interceptor and sequences, including cancelling and overwriting the return value of the call\r
2354     // private\r
2355     function getMethodEvent(method){\r
2356         var e = (this.methodEvents = this.methodEvents ||\r
2357         {})[method], returnValue, v, cancel, obj = this;\r
2358 \r
2359         if (!e) {\r
2360             this.methodEvents[method] = e = {};\r
2361             e.originalFn = this[method];\r
2362             e.methodName = method;\r
2363             e.before = [];\r
2364             e.after = [];\r
2365 \r
2366             var makeCall = function(fn, scope, args){\r
2367                 if (!Ext.isEmpty(v = fn.apply(scope || obj, args))) {\r
2368                     if (Ext.isObject(v)) {\r
2369                         returnValue = !Ext.isEmpty(v.returnValue) ? v.returnValue : v;\r
2370                         cancel = !!v.cancel;\r
2371                     }\r
2372                     else\r
2373                         if (v === false) {\r
2374                             cancel = true;\r
2375                         }\r
2376                         else {\r
2377                             returnValue = v;\r
2378                         }\r
2379                 }\r
2380             };\r
2381 \r
2382             this[method] = function(){\r
2383                 var args = Ext.toArray(arguments);\r
2384                 returnValue = v = undefined;\r
2385                 cancel = false;\r
2386 \r
2387                 Ext.each(e.before, function(b){\r
2388                     makeCall(b.fn, b.scope, args);\r
2389                     if (cancel) {\r
2390                         return returnValue;\r
2391                     }\r
2392                 });\r
2393 \r
2394                 if (!Ext.isEmpty(v = e.originalFn.apply(obj, args))) {\r
2395                     returnValue = v;\r
2396                 }\r
2397                 Ext.each(e.after, function(a){\r
2398                     makeCall(a.fn, a.scope, args);\r
2399                     if (cancel) {\r
2400                         return returnValue;\r
2401                     }\r
2402                 });\r
2403                 return returnValue;\r
2404             };\r
2405         }\r
2406         return e;\r
2407     }\r
2408 \r
2409     return {\r
2410         // these are considered experimental\r
2411         // allows for easier interceptor and sequences, including cancelling and overwriting the return value of the call\r
2412         // adds an 'interceptor' called before the original method\r
2413         beforeMethod : function(method, fn, scope){\r
2414             getMethodEvent.call(this, method).before.push({\r
2415                 fn: fn,\r
2416                 scope: scope\r
2417             });\r
2418         },\r
2419 \r
2420         // adds a 'sequence' called after the original method\r
2421         afterMethod : function(method, fn, scope){\r
2422             getMethodEvent.call(this, method).after.push({\r
2423                 fn: fn,\r
2424                 scope: scope\r
2425             });\r
2426         },\r
2427 \r
2428         removeMethodListener: function(method, fn, scope){\r
2429             var e = getMethodEvent.call(this, method), found = false;\r
2430             Ext.each(e.before, function(b, i, arr){\r
2431                 if (b.fn == fn && b.scope == scope) {\r
2432                     arr.splice(i, 1);\r
2433                     found = true;\r
2434                     return false;\r
2435                 }\r
2436             });\r
2437             if (!found) {\r
2438                 Ext.each(e.after, function(a, i, arr){\r
2439                     if (a.fn == fn && a.scope == scope) {\r
2440                         arr.splice(i, 1);\r
2441                         return false;\r
2442                     }\r
2443                 });\r
2444             }\r
2445         },\r
2446 \r
2447         /**\r
2448          * Relays selected events from the specified Observable as if the events were fired by <tt><b>this</b></tt>.\r
2449          * @param {Object} o The Observable whose events this object is to relay.\r
2450          * @param {Array} events Array of event names to relay.\r
2451          */\r
2452         relayEvents : function(o, events){\r
2453             var me = this;\r
2454             function createHandler(ename){\r
2455                 return function(){\r
2456                     return me.fireEvent.apply(me, [ename].concat(Ext.toArray(arguments)));\r
2457                 };\r
2458             }\r
2459             Ext.each(events, function(ename){\r
2460                 me.events[ename] = me.events[ename] || true;\r
2461                 o.on(ename, createHandler(ename), me);\r
2462             });\r
2463         },\r
2464 \r
2465         /**\r
2466          * <p>Enables events fired by this Observable to bubble up an owner hierarchy by calling\r
2467          * <code>this.getBubbleTarget()</code> if present. There is no implementation in the Observable base class.</p>\r
2468          * <p>This is commonly used by Ext.Components to bubble events to owner Containers. See {@link Ext.Component.getBubbleTarget}. The default\r
2469          * implementation in Ext.Component returns the Component's immediate owner. But if a known target is required, this can be overridden to\r
2470          * access the required target more quickly.</p>\r
2471          * <p>Example:</p><pre><code>\r
2472 Ext.override(Ext.form.Field, {\r
2473     //  Add functionality to Field&#39;s initComponent to enable the change event to bubble\r
2474     initComponent : Ext.form.Field.prototype.initComponent.createSequence(function() {\r
2475         this.enableBubble('change');\r
2476     }),\r
2477 \r
2478     //  We know that we want Field&#39;s events to bubble directly to the FormPanel.\r
2479     getBubbleTarget : function() {\r
2480         if (!this.formPanel) {\r
2481             this.formPanel = this.findParentByType('form');\r
2482         }\r
2483         return this.formPanel;\r
2484     }\r
2485 });\r
2486 \r
2487 var myForm = new Ext.formPanel({\r
2488     title: 'User Details',\r
2489     items: [{\r
2490         ...\r
2491     }],\r
2492     listeners: {\r
2493         change: function() {\r
2494             // Title goes red if form has been modified.\r
2495             myForm.header.setStyle('color', 'red');\r
2496         }\r
2497     }\r
2498 });\r
2499 </code></pre>\r
2500          * @param {String/Array} events The event name to bubble, or an Array of event names.\r
2501          */\r
2502         enableBubble : function(events){\r
2503             var me = this;\r
2504             if(!Ext.isEmpty(events)){\r
2505                 events = Ext.isArray(events) ? events : Ext.toArray(arguments);\r
2506                 Ext.each(events, function(ename){\r
2507                     ename = ename.toLowerCase();\r
2508                     var ce = me.events[ename] || true;\r
2509                     if (Ext.isBoolean(ce)) {\r
2510                         ce = new Ext.util.Event(me, ename);\r
2511                         me.events[ename] = ce;\r
2512                     }\r
2513                     ce.bubble = true;\r
2514                 });\r
2515             }\r
2516         }\r
2517     };\r
2518 }());\r
2519 \r
2520 \r
2521 /**\r
2522  * Starts capture on the specified Observable. All events will be passed\r
2523  * to the supplied function with the event name + standard signature of the event\r
2524  * <b>before</b> the event is fired. If the supplied function returns false,\r
2525  * the event will not fire.\r
2526  * @param {Observable} o The Observable to capture events from.\r
2527  * @param {Function} fn The function to call when an event is fired.\r
2528  * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the Observable firing the event.\r
2529  * @static\r
2530  */\r
2531 Ext.util.Observable.capture = function(o, fn, scope){\r
2532     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);\r
2533 };\r
2534 \r
2535 \r
2536 /**\r
2537  * Sets observability on the passed class constructor.<p>\r
2538  * <p>This makes any event fired on any instance of the passed class also fire a single event through\r
2539  * the <i>class</i> allowing for central handling of events on many instances at once.</p>\r
2540  * <p>Usage:</p><pre><code>\r
2541 Ext.util.Observable.observeClass(Ext.data.Connection);\r
2542 Ext.data.Connection.on('beforerequest', function(con, options) {\r
2543     console.log('Ajax request made to ' + options.url);\r
2544 });</code></pre>\r
2545  * @param {Function} c The class constructor to make observable.\r
2546  * @param {Object} listeners An object containing a series of listeners to add. See {@link #addListener}. \r
2547  * @static\r
2548  */\r
2549 Ext.util.Observable.observeClass = function(c, listeners){\r
2550     if(c){\r
2551       if(!c.fireEvent){\r
2552           Ext.apply(c, new Ext.util.Observable());\r
2553           Ext.util.Observable.capture(c.prototype, c.fireEvent, c);\r
2554       }\r
2555       if(Ext.isObject(listeners)){\r
2556           c.on(listeners);\r
2557       }\r
2558       return c;\r
2559    }\r
2560 };/**\r
2561  * @class Ext.EventManager\r
2562  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides\r
2563  * several useful events directly.\r
2564  * See {@link Ext.EventObject} for more details on normalized event objects.\r
2565  * @singleton\r
2566  */\r
2567 Ext.EventManager = function(){\r
2568     var docReadyEvent,\r
2569         docReadyProcId,\r
2570         docReadyState = false,\r
2571         E = Ext.lib.Event,\r
2572         D = Ext.lib.Dom,\r
2573         DOC = document,\r
2574         WINDOW = window,\r
2575         IEDEFERED = "ie-deferred-loader",\r
2576         DOMCONTENTLOADED = "DOMContentLoaded",\r
2577         propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/,\r
2578         /*\r
2579          * This cache is used to hold special js objects, the document and window, that don't have an id. We need to keep\r
2580          * a reference to them so we can look them up at a later point.\r
2581          */\r
2582         specialElCache = [];\r
2583 \r
2584      function getId(el){\r
2585         var id = false,\r
2586             i = 0,\r
2587             len = specialElCache.length,\r
2588             id = false,\r
2589             skip = false,\r
2590             o;\r
2591         if(el){\r
2592             if(el.getElementById || el.navigator){\r
2593                 // look up the id\r
2594                 for(; i < len; ++i){\r
2595                     o = specialElCache[i];\r
2596                     if(o.el === el){\r
2597                         id = o.id;\r
2598                         break;\r
2599                     }\r
2600                 }\r
2601                 if(!id){\r
2602                     // for browsers that support it, ensure that give the el the same id\r
2603                     id = Ext.id(el);\r
2604                     specialElCache.push({\r
2605                         id: id,\r
2606                         el: el\r
2607                     });\r
2608                     skip = true;\r
2609                 }\r
2610             }else{\r
2611                 id = Ext.id(el);\r
2612             }\r
2613             if(!Ext.elCache[id]){\r
2614                 Ext.Element.addToCache(new Ext.Element(el), id);\r
2615                 if(skip){\r
2616                     Ext.elCache[id].skipGC = true;\r
2617                 }\r
2618             }\r
2619         }\r
2620         return id;\r
2621      };\r
2622 \r
2623     /// There is some jquery work around stuff here that isn't needed in Ext Core.\r
2624     function addListener(el, ename, fn, wrap, scope){\r
2625         el = Ext.getDom(el);\r
2626         var id = getId(el),\r
2627             es = Ext.elCache[id].events,\r
2628             wfn;\r
2629 \r
2630         wfn = E.on(el, ename, wrap);\r
2631         es[ename] = es[ename] || [];\r
2632         es[ename].push([fn, wrap, scope, wfn]);\r
2633 \r
2634         // this is a workaround for jQuery and should somehow be removed from Ext Core in the future\r
2635         // without breaking ExtJS.\r
2636         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery\r
2637             var args = ["DOMMouseScroll", wrap, false];\r
2638             el.addEventListener.apply(el, args);\r
2639             Ext.EventManager.addListener(WINDOW, 'unload', function(){\r
2640                 el.removeEventListener.apply(el, args);\r
2641             });\r
2642         }\r
2643         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document\r
2644             Ext.EventManager.stoppedMouseDownEvent.addListener(wrap);\r
2645         }\r
2646     };\r
2647 \r
2648     function fireDocReady(){\r
2649         if(!docReadyState){\r
2650             Ext.isReady = docReadyState = true;\r
2651             if(docReadyProcId){\r
2652                 clearInterval(docReadyProcId);\r
2653             }\r
2654             if(Ext.isGecko || Ext.isOpera) {\r
2655                 DOC.removeEventListener(DOMCONTENTLOADED, fireDocReady, false);\r
2656             }\r
2657             if(Ext.isIE){\r
2658                 var defer = DOC.getElementById(IEDEFERED);\r
2659                 if(defer){\r
2660                     defer.onreadystatechange = null;\r
2661                     defer.parentNode.removeChild(defer);\r
2662                 }\r
2663             }\r
2664             if(docReadyEvent){\r
2665                 docReadyEvent.fire();\r
2666                 docReadyEvent.listeners = []; // clearListeners no longer compatible.  Force single: true?\r
2667             }\r
2668         }\r
2669     };\r
2670 \r
2671     function initDocReady(){\r
2672         var COMPLETE = "complete";\r
2673 \r
2674         docReadyEvent = new Ext.util.Event();\r
2675         if (Ext.isGecko || Ext.isOpera) {\r
2676             DOC.addEventListener(DOMCONTENTLOADED, fireDocReady, false);\r
2677         } else if (Ext.isIE){\r
2678             DOC.write("<s"+'cript id=' + IEDEFERED + ' defer="defer" src="/'+'/:"></s'+"cript>");\r
2679             DOC.getElementById(IEDEFERED).onreadystatechange = function(){\r
2680                 if(this.readyState == COMPLETE){\r
2681                     fireDocReady();\r
2682                 }\r
2683             };\r
2684         } else if (Ext.isWebKit){\r
2685             docReadyProcId = setInterval(function(){\r
2686                 if(DOC.readyState == COMPLETE) {\r
2687                     fireDocReady();\r
2688                  }\r
2689             }, 10);\r
2690         }\r
2691         // no matter what, make sure it fires on load\r
2692         E.on(WINDOW, "load", fireDocReady);\r
2693     };\r
2694 \r
2695     function createTargeted(h, o){\r
2696         return function(){\r
2697             var args = Ext.toArray(arguments);\r
2698             if(o.target == Ext.EventObject.setEvent(args[0]).target){\r
2699                 h.apply(this, args);\r
2700             }\r
2701         };\r
2702     };\r
2703 \r
2704     function createBuffered(h, o, fn){\r
2705         fn.task = new Ext.util.DelayedTask(h);\r
2706         var w = function(e){\r
2707             // create new event object impl so new events don't wipe out properties\r
2708             fn.task.delay(o.buffer, h, null, [new Ext.EventObjectImpl(e)]);\r
2709         };\r
2710         return w;\r
2711     };\r
2712 \r
2713     function createSingle(h, el, ename, fn, scope){\r
2714         return function(e){\r
2715             Ext.EventManager.removeListener(el, ename, fn, scope);\r
2716             h(e);\r
2717         };\r
2718     };\r
2719 \r
2720     function createDelayed(h, o, fn){\r
2721         return function(e){\r
2722             var task = new Ext.util.DelayedTask(h);\r
2723             if(!fn.tasks) {\r
2724                 fn.tasks = [];\r
2725             }\r
2726             fn.tasks.push(task);\r
2727             task.delay(o.delay || 10, h, null, [new Ext.EventObjectImpl(e)]);\r
2728         };\r
2729     };\r
2730 \r
2731     function listen(element, ename, opt, fn, scope){\r
2732         var o = !Ext.isObject(opt) ? {} : opt,\r
2733             el = Ext.getDom(element);\r
2734 \r
2735         fn = fn || o.fn;\r
2736         scope = scope || o.scope;\r
2737 \r
2738         if(!el){\r
2739             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';\r
2740         }\r
2741         function h(e){\r
2742             // prevent errors while unload occurring\r
2743             if(!Ext){// !window[xname]){  ==> can't we do this?\r
2744                 return;\r
2745             }\r
2746             e = Ext.EventObject.setEvent(e);\r
2747             var t;\r
2748             if (o.delegate) {\r
2749                 if(!(t = e.getTarget(o.delegate, el))){\r
2750                     return;\r
2751                 }\r
2752             } else {\r
2753                 t = e.target;\r
2754             }\r
2755             if (o.stopEvent) {\r
2756                 e.stopEvent();\r
2757             }\r
2758             if (o.preventDefault) {\r
2759                e.preventDefault();\r
2760             }\r
2761             if (o.stopPropagation) {\r
2762                 e.stopPropagation();\r
2763             }\r
2764             if (o.normalized) {\r
2765                 e = e.browserEvent;\r
2766             }\r
2767 \r
2768             fn.call(scope || el, e, t, o);\r
2769         };\r
2770         if(o.target){\r
2771             h = createTargeted(h, o);\r
2772         }\r
2773         if(o.delay){\r
2774             h = createDelayed(h, o, fn);\r
2775         }\r
2776         if(o.single){\r
2777             h = createSingle(h, el, ename, fn, scope);\r
2778         }\r
2779         if(o.buffer){\r
2780             h = createBuffered(h, o, fn);\r
2781         }\r
2782 \r
2783         addListener(el, ename, fn, h, scope);\r
2784         return h;\r
2785     };\r
2786 \r
2787     var pub = {\r
2788         /**\r
2789          * Appends an event handler to an element.  The shorthand version {@link #on} is equivalent.  Typically you will\r
2790          * use {@link Ext.Element#addListener} directly on an Element in favor of calling this version.\r
2791          * @param {String/HTMLElement} el The html element or id to assign the event handler to.\r
2792          * @param {String} eventName The name of the event to listen for.\r
2793          * @param {Function} handler The handler function the event invokes. This function is passed\r
2794          * the following parameters:<ul>\r
2795          * <li>evt : EventObject<div class="sub-desc">The {@link Ext.EventObject EventObject} describing the event.</div></li>\r
2796          * <li>t : Element<div class="sub-desc">The {@link Ext.Element Element} which was the target of the event.\r
2797          * Note that this may be filtered by using the <tt>delegate</tt> option.</div></li>\r
2798          * <li>o : Object<div class="sub-desc">The options object from the addListener call.</div></li>\r
2799          * </ul>\r
2800          * @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
2801          * @param {Object} options (optional) An object containing handler configuration properties.\r
2802          * This may contain any of the following properties:<ul>\r
2803          * <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
2804          * <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
2805          * <li>stopEvent : Boolean<div class="sub-desc">True to stop the event. That is stop propagation, and prevent the default action.</div></li>\r
2806          * <li>preventDefault : Boolean<div class="sub-desc">True to prevent the default action</div></li>\r
2807          * <li>stopPropagation : Boolean<div class="sub-desc">True to prevent event propagation</div></li>\r
2808          * <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
2809          * <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
2810          * <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
2811          * <li>buffer : Number<div class="sub-desc">Causes the handler to be scheduled to run in an {@link Ext.util.DelayedTask} delayed\r
2812          * by the specified number of milliseconds. If the event fires again within that time, the original\r
2813          * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</div></li>\r
2814          * <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
2815          * </ul><br>\r
2816          * <p>See {@link Ext.Element#addListener} for examples of how to use these options.</p>\r
2817          */\r
2818         addListener : function(element, eventName, fn, scope, options){\r
2819             if(Ext.isObject(eventName)){\r
2820                 var o = eventName, e, val;\r
2821                 for(e in o){\r
2822                     val = o[e];\r
2823                     if(!propRe.test(e)){\r
2824                         if(Ext.isFunction(val)){\r
2825                             // shared options\r
2826                             listen(element, e, o, val, o.scope);\r
2827                         }else{\r
2828                             // individual options\r
2829                             listen(element, e, val);\r
2830                         }\r
2831                     }\r
2832                 }\r
2833             } else {\r
2834                 listen(element, eventName, options, fn, scope);\r
2835             }\r
2836         },\r
2837 \r
2838         /**\r
2839          * Removes an event handler from an element.  The shorthand version {@link #un} is equivalent.  Typically\r
2840          * you will use {@link Ext.Element#removeListener} directly on an Element in favor of calling this version.\r
2841          * @param {String/HTMLElement} el The id or html element from which to remove the listener.\r
2842          * @param {String} eventName The name of the event.\r
2843          * @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
2844          * @param {Object} scope If a scope (<b><code>this</code></b> reference) was specified when the listener was added,\r
2845          * then this must refer to the same object.\r
2846          */\r
2847         removeListener : function(el, eventName, fn, scope){\r
2848             el = Ext.getDom(el);\r
2849             var id = getId(el),\r
2850                 f = el && (Ext.elCache[id].events)[eventName] || [],\r
2851                 wrap, i, l, k, wf;\r
2852 \r
2853             for (i = 0, len = f.length; i < len; i++) {\r
2854                 if (Ext.isArray(f[i]) && f[i][0] == fn && (!scope || f[i][2] == scope)) {\r
2855                     if(fn.task) {\r
2856                         fn.task.cancel();\r
2857                         delete fn.task;\r
2858                     }\r
2859                     k = fn.tasks && fn.tasks.length;\r
2860                     if(k) {\r
2861                         while(k--) {\r
2862                             fn.tasks[k].cancel();\r
2863                         }\r
2864                         delete fn.tasks;\r
2865                     }\r
2866                     wf = wrap = f[i][1];\r
2867                     if (E.extAdapter) {\r
2868                         wf = f[i][3];\r
2869                     }\r
2870                     E.un(el, eventName, wf);\r
2871                     f.splice(i,1);\r
2872                     if (f.length === 0) {\r
2873                         delete Ext.elCache[id].events[eventName];\r
2874                     }\r
2875                     for (k in Ext.elCache[id].events) {\r
2876                         return false;\r
2877                     }\r
2878                     Ext.elCache[id].events = {};\r
2879                     return false;\r
2880                 }\r
2881             }\r
2882 \r
2883             // jQuery workaround that should be removed from Ext Core\r
2884             if(eventName == "mousewheel" && el.addEventListener && wrap){\r
2885                 el.removeEventListener("DOMMouseScroll", wrap, false);\r
2886             }\r
2887 \r
2888             if(eventName == "mousedown" && el == DOC && wrap){ // fix stopped mousedowns on the document\r
2889                 Ext.EventManager.stoppedMouseDownEvent.removeListener(wrap);\r
2890             }\r
2891         },\r
2892 \r
2893         /**\r
2894          * Removes all event handers from an element.  Typically you will use {@link Ext.Element#removeAllListeners}\r
2895          * directly on an Element in favor of calling this version.\r
2896          * @param {String/HTMLElement} el The id or html element from which to remove all event handlers.\r
2897          */\r
2898         removeAll : function(el){\r
2899             el = Ext.getDom(el);\r
2900             var id = getId(el),\r
2901                 ec = Ext.elCache[id] || {},\r
2902                 es = ec.events || {},\r
2903                 f, i, len, ename, fn, k;\r
2904 \r
2905             for(ename in es){\r
2906                 if(es.hasOwnProperty(ename)){\r
2907                     f = es[ename];\r
2908                     for (i = 0, len = f.length; i < len; i++) {\r
2909                         fn = f[i][0];\r
2910                         if(fn.task) {\r
2911                             fn.task.cancel();\r
2912                             delete fn.task;\r
2913                         }\r
2914                         if(fn.tasks && (k = fn.tasks.length)) {\r
2915                             while(k--) {\r
2916                                 fn.tasks[k].cancel();\r
2917                             }\r
2918                             delete fn.tasks;\r
2919                         }\r
2920                         E.un(el, ename, E.extAdapter ? f[i][3] : f[i][1]);\r
2921                     }\r
2922                 }\r
2923             }\r
2924             if (Ext.elCache[id]) {\r
2925                 Ext.elCache[id].events = {};\r
2926             }\r
2927         },\r
2928 \r
2929         getListeners : function(el, eventName) {\r
2930             el = Ext.getDom(el);\r
2931             var id = getId(el),\r
2932                 ec = Ext.elCache[id] || {},\r
2933                 es = ec.events || {},\r
2934                 results = [];\r
2935             if (es && es[eventName]) {\r
2936                 return es[eventName];\r
2937             } else {\r
2938                 return null;\r
2939             }\r
2940         },\r
2941 \r
2942         purgeElement : function(el, recurse, eventName) {\r
2943             el = Ext.getDom(el);\r
2944             var id = getId(el),\r
2945                 ec = Ext.elCache[id] || {},\r
2946                 es = ec.events || {},\r
2947                 i, f, len;\r
2948             if (eventName) {\r
2949                 if (es && es.hasOwnProperty(eventName)) {\r
2950                     f = es[eventName];\r
2951                     for (i = 0, len = f.length; i < len; i++) {\r
2952                         Ext.EventManager.removeListener(el, eventName, f[i][0]);\r
2953                     }\r
2954                 }\r
2955             } else {\r
2956                 Ext.EventManager.removeAll(el);\r
2957             }\r
2958             if (recurse && el && el.childNodes) {\r
2959                 for (i = 0, len = el.childNodes.length; i < len; i++) {\r
2960                     Ext.EventManager.purgeElement(el.childNodes[i], recurse, eventName);\r
2961                 }\r
2962             }\r
2963         },\r
2964 \r
2965         _unload : function() {\r
2966             var el;\r
2967             for (el in Ext.elCache) {\r
2968                 Ext.EventManager.removeAll(el);\r
2969             }\r
2970         },\r
2971         /**\r
2972          * Adds a listener to be notified when the document is ready (before onload and before images are loaded). Can be\r
2973          * accessed shorthanded as Ext.onReady().\r
2974          * @param {Function} fn The method the event invokes.\r
2975          * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the handler function executes. Defaults to the browser window.\r
2976          * @param {boolean} options (optional) Options object as passed to {@link Ext.Element#addListener}. It is recommended that the options\r
2977          * <code>{single: true}</code> be used so that the handler is removed on first invocation.\r
2978          */\r
2979         onDocumentReady : function(fn, scope, options){\r
2980             if(docReadyState){ // if it already fired\r
2981                 docReadyEvent.addListener(fn, scope, options);\r
2982                 docReadyEvent.fire();\r
2983                 docReadyEvent.listeners = []; // clearListeners no longer compatible.  Force single: true?\r
2984             } else {\r
2985                 if(!docReadyEvent) initDocReady();\r
2986                 options = options || {};\r
2987                 options.delay = options.delay || 1;\r
2988                 docReadyEvent.addListener(fn, scope, options);\r
2989             }\r
2990         }\r
2991     };\r
2992      /**\r
2993      * Appends an event handler to an element.  Shorthand for {@link #addListener}.\r
2994      * @param {String/HTMLElement} el The html element or id to assign the event handler to\r
2995      * @param {String} eventName The name of the event to listen for.\r
2996      * @param {Function} handler The handler function the event invokes.\r
2997      * @param {Object} scope (optional) (<code>this</code> reference) in which the handler function executes. <b>Defaults to the Element</b>.\r
2998      * @param {Object} options (optional) An object containing standard {@link #addListener} options\r
2999      * @member Ext.EventManager\r
3000      * @method on\r
3001      */\r
3002     pub.on = pub.addListener;\r
3003     /**\r
3004      * Removes an event handler from an element.  Shorthand for {@link #removeListener}.\r
3005      * @param {String/HTMLElement} el The id or html element from which to remove the listener.\r
3006      * @param {String} eventName The name of the event.\r
3007      * @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
3008      * @param {Object} scope If a scope (<b><code>this</code></b> reference) was specified when the listener was added,\r
3009      * then this must refer to the same object.\r
3010      * @member Ext.EventManager\r
3011      * @method un\r
3012      */\r
3013     pub.un = pub.removeListener;\r
3014 \r
3015     pub.stoppedMouseDownEvent = new Ext.util.Event();\r
3016     return pub;\r
3017 }();\r
3018 /**\r
3019   * 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
3020   * @param {Function} fn The method the event invokes.\r
3021   * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the handler function executes. Defaults to the browser window.\r
3022   * @param {boolean} options (optional) Options object as passed to {@link Ext.Element#addListener}. It is recommended that the options\r
3023   * <code>{single: true}</code> be used so that the handler is removed on first invocation.\r
3024   * @member Ext\r
3025   * @method onReady\r
3026  */\r
3027 Ext.onReady = Ext.EventManager.onDocumentReady;\r
3028 \r
3029 \r
3030 //Initialize doc classes\r
3031 (function(){\r
3032 \r
3033     var initExtCss = function(){\r
3034         // find the body element\r
3035         var bd = document.body || document.getElementsByTagName('body')[0];\r
3036         if(!bd){ return false; }\r
3037         var cls = [' ',\r
3038                 Ext.isIE ? "ext-ie " + (Ext.isIE6 ? 'ext-ie6' : (Ext.isIE7 ? 'ext-ie7' : 'ext-ie8'))\r
3039                 : Ext.isGecko ? "ext-gecko " + (Ext.isGecko2 ? 'ext-gecko2' : 'ext-gecko3')\r
3040                 : Ext.isOpera ? "ext-opera"\r
3041                 : Ext.isWebKit ? "ext-webkit" : ""];\r
3042 \r
3043         if(Ext.isSafari){\r
3044             cls.push("ext-safari " + (Ext.isSafari2 ? 'ext-safari2' : (Ext.isSafari3 ? 'ext-safari3' : 'ext-safari4')));\r
3045         }else if(Ext.isChrome){\r
3046             cls.push("ext-chrome");\r
3047         }\r
3048 \r
3049         if(Ext.isMac){\r
3050             cls.push("ext-mac");\r
3051         }\r
3052         if(Ext.isLinux){\r
3053             cls.push("ext-linux");\r
3054         }\r
3055 \r
3056         if(Ext.isStrict || Ext.isBorderBox){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"\r
3057             var p = bd.parentNode;\r
3058             if(p){\r
3059                 p.className += Ext.isStrict ? ' ext-strict' : ' ext-border-box';\r
3060             }\r
3061         }\r
3062         bd.className += cls.join(' ');\r
3063         return true;\r
3064     }\r
3065 \r
3066     if(!initExtCss()){\r
3067         Ext.onReady(initExtCss);\r
3068     }\r
3069 })();\r
3070 \r
3071 \r
3072 /**\r
3073  * @class Ext.EventObject\r
3074  * Just as {@link Ext.Element} wraps around a native DOM node, Ext.EventObject\r
3075  * wraps the browser's native event-object normalizing cross-browser differences,\r
3076  * such as which mouse button is clicked, keys pressed, mechanisms to stop\r
3077  * event-propagation along with a method to prevent default actions from taking place.\r
3078  * <p>For example:</p>\r
3079  * <pre><code>\r
3080 function handleClick(e, t){ // e is not a standard event object, it is a Ext.EventObject\r
3081     e.preventDefault();\r
3082     var target = e.getTarget(); // same as t (the target HTMLElement)\r
3083     ...\r
3084 }\r
3085 var myDiv = {@link Ext#get Ext.get}("myDiv");  // get reference to an {@link Ext.Element}\r
3086 myDiv.on(         // 'on' is shorthand for addListener\r
3087     "click",      // perform an action on click of myDiv\r
3088     handleClick   // reference to the action handler\r
3089 );\r
3090 // other methods to do the same:\r
3091 Ext.EventManager.on("myDiv", 'click', handleClick);\r
3092 Ext.EventManager.addListener("myDiv", 'click', handleClick);\r
3093  </code></pre>\r
3094  * @singleton\r
3095  */\r
3096 Ext.EventObject = function(){\r
3097     var E = Ext.lib.Event,\r
3098         // safari keypress events for special keys return bad keycodes\r
3099         safariKeys = {\r
3100             3 : 13, // enter\r
3101             63234 : 37, // left\r
3102             63235 : 39, // right\r
3103             63232 : 38, // up\r
3104             63233 : 40, // down\r
3105             63276 : 33, // page up\r
3106             63277 : 34, // page down\r
3107             63272 : 46, // delete\r
3108             63273 : 36, // home\r
3109             63275 : 35  // end\r
3110         },\r
3111         // normalize button clicks\r
3112         btnMap = Ext.isIE ? {1:0,4:1,2:2} :\r
3113                 (Ext.isWebKit ? {1:0,2:1,3:2} : {0:0,1:1,2:2});\r
3114 \r
3115     Ext.EventObjectImpl = function(e){\r
3116         if(e){\r
3117             this.setEvent(e.browserEvent || e);\r
3118         }\r
3119     };\r
3120 \r
3121     Ext.EventObjectImpl.prototype = {\r
3122            /** @private */\r
3123         setEvent : function(e){\r
3124             var me = this;\r
3125             if(e == me || (e && e.browserEvent)){ // already wrapped\r
3126                 return e;\r
3127             }\r
3128             me.browserEvent = e;\r
3129             if(e){\r
3130                 // normalize buttons\r
3131                 me.button = e.button ? btnMap[e.button] : (e.which ? e.which - 1 : -1);\r
3132                 if(e.type == 'click' && me.button == -1){\r
3133                     me.button = 0;\r
3134                 }\r
3135                 me.type = e.type;\r
3136                 me.shiftKey = e.shiftKey;\r
3137                 // mac metaKey behaves like ctrlKey\r
3138                 me.ctrlKey = e.ctrlKey || e.metaKey || false;\r
3139                 me.altKey = e.altKey;\r
3140                 // in getKey these will be normalized for the mac\r
3141                 me.keyCode = e.keyCode;\r
3142                 me.charCode = e.charCode;\r
3143                 // cache the target for the delayed and or buffered events\r
3144                 me.target = E.getTarget(e);\r
3145                 // same for XY\r
3146                 me.xy = E.getXY(e);\r
3147             }else{\r
3148                 me.button = -1;\r
3149                 me.shiftKey = false;\r
3150                 me.ctrlKey = false;\r
3151                 me.altKey = false;\r
3152                 me.keyCode = 0;\r
3153                 me.charCode = 0;\r
3154                 me.target = null;\r
3155                 me.xy = [0, 0];\r
3156             }\r
3157             return me;\r
3158         },\r
3159 \r
3160         /**\r
3161          * Stop the event (preventDefault and stopPropagation)\r
3162          */\r
3163         stopEvent : function(){\r
3164             var me = this;\r
3165             if(me.browserEvent){\r
3166                 if(me.browserEvent.type == 'mousedown'){\r
3167                     Ext.EventManager.stoppedMouseDownEvent.fire(me);\r
3168                 }\r
3169                 E.stopEvent(me.browserEvent);\r
3170             }\r
3171         },\r
3172 \r
3173         /**\r
3174          * Prevents the browsers default handling of the event.\r
3175          */\r
3176         preventDefault : function(){\r
3177             if(this.browserEvent){\r
3178                 E.preventDefault(this.browserEvent);\r
3179             }\r
3180         },\r
3181 \r
3182         /**\r
3183          * Cancels bubbling of the event.\r
3184          */\r
3185         stopPropagation : function(){\r
3186             var me = this;\r
3187             if(me.browserEvent){\r
3188                 if(me.browserEvent.type == 'mousedown'){\r
3189                     Ext.EventManager.stoppedMouseDownEvent.fire(me);\r
3190                 }\r
3191                 E.stopPropagation(me.browserEvent);\r
3192             }\r
3193         },\r
3194 \r
3195         /**\r
3196          * Gets the character code for the event.\r
3197          * @return {Number}\r
3198          */\r
3199         getCharCode : function(){\r
3200             return this.charCode || this.keyCode;\r
3201         },\r
3202 \r
3203         /**\r
3204          * Returns a normalized keyCode for the event.\r
3205          * @return {Number} The key code\r
3206          */\r
3207         getKey : function(){\r
3208             return this.normalizeKey(this.keyCode || this.charCode)\r
3209         },\r
3210 \r
3211         // private\r
3212         normalizeKey: function(k){\r
3213             return Ext.isSafari ? (safariKeys[k] || k) : k;\r
3214         },\r
3215 \r
3216         /**\r
3217          * Gets the x coordinate of the event.\r
3218          * @return {Number}\r
3219          */\r
3220         getPageX : function(){\r
3221             return this.xy[0];\r
3222         },\r
3223 \r
3224         /**\r
3225          * Gets the y coordinate of the event.\r
3226          * @return {Number}\r
3227          */\r
3228         getPageY : function(){\r
3229             return this.xy[1];\r
3230         },\r
3231 \r
3232         /**\r
3233          * Gets the page coordinates of the event.\r
3234          * @return {Array} The xy values like [x, y]\r
3235          */\r
3236         getXY : function(){\r
3237             return this.xy;\r
3238         },\r
3239 \r
3240         /**\r
3241          * Gets the target for the event.\r
3242          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target\r
3243          * @param {Number/Mixed} maxDepth (optional) The max depth to\r
3244                 search as a number or element (defaults to 10 || document.body)\r
3245          * @param {Boolean} returnEl (optional) True to return a Ext.Element object instead of DOM node\r
3246          * @return {HTMLelement}\r
3247          */\r
3248         getTarget : function(selector, maxDepth, returnEl){\r
3249             return selector ? Ext.fly(this.target).findParent(selector, maxDepth, returnEl) : (returnEl ? Ext.get(this.target) : this.target);\r
3250         },\r
3251 \r
3252         /**\r
3253          * Gets the related target.\r
3254          * @return {HTMLElement}\r
3255          */\r
3256         getRelatedTarget : function(){\r
3257             return this.browserEvent ? E.getRelatedTarget(this.browserEvent) : null;\r
3258         },\r
3259 \r
3260         /**\r
3261          * Normalizes mouse wheel delta across browsers\r
3262          * @return {Number} The delta\r
3263          */\r
3264         getWheelDelta : function(){\r
3265             var e = this.browserEvent;\r
3266             var delta = 0;\r
3267             if(e.wheelDelta){ /* IE/Opera. */\r
3268                 delta = e.wheelDelta/120;\r
3269             }else if(e.detail){ /* Mozilla case. */\r
3270                 delta = -e.detail/3;\r
3271             }\r
3272             return delta;\r
3273         },\r
3274 \r
3275         /**\r
3276         * 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
3277         * Example usage:<pre><code>\r
3278         // Handle click on any child of an element\r
3279         Ext.getBody().on('click', function(e){\r
3280             if(e.within('some-el')){\r
3281                 alert('Clicked on a child of some-el!');\r
3282             }\r
3283         });\r
3284 \r
3285         // Handle click directly on an element, ignoring clicks on child nodes\r
3286         Ext.getBody().on('click', function(e,t){\r
3287             if((t.id == 'some-el') && !e.within(t, true)){\r
3288                 alert('Clicked directly on some-el!');\r
3289             }\r
3290         });\r
3291         </code></pre>\r
3292          * @param {Mixed} el The id, DOM element or Ext.Element to check\r
3293          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target\r
3294          * @param {Boolean} allowEl {optional} true to also check if the passed element is the target or related target\r
3295          * @return {Boolean}\r
3296          */\r
3297         within : function(el, related, allowEl){\r
3298             if(el){\r
3299                 var t = this[related ? "getRelatedTarget" : "getTarget"]();\r
3300                 return t && ((allowEl ? (t == Ext.getDom(el)) : false) || Ext.fly(el).contains(t));\r
3301             }\r
3302             return false;\r
3303         }\r
3304      };\r
3305 \r
3306     return new Ext.EventObjectImpl();\r
3307 }();\r
3308 \r
3309 /**
3310 * @class Ext.EventManager
3311 */
3312 Ext.apply(Ext.EventManager, function(){
3313    var resizeEvent,
3314        resizeTask,
3315        textEvent,
3316        textSize,
3317        D = Ext.lib.Dom,
3318        propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/,
3319        curWidth = 0,
3320        curHeight = 0,
3321        // note 1: IE fires ONLY the keydown event on specialkey autorepeat
3322        // note 2: Safari < 3.1, Gecko (Mac/Linux) & Opera fire only the keypress event on specialkey autorepeat
3323        // (research done by @Jan Wolter at http://unixpapa.com/js/key.html)
3324        useKeydown = Ext.isWebKit ?
3325                    Ext.num(navigator.userAgent.match(/AppleWebKit\/(\d+)/)[1]) >= 525 :
3326                    !((Ext.isGecko && !Ext.isWindows) || Ext.isOpera);
3327
3328    return {
3329        // private
3330        doResizeEvent: function(){
3331            var h = D.getViewHeight(),
3332                w = D.getViewWidth();
3333
3334            //whacky problem in IE where the resize event will fire even though the w/h are the same.
3335            if(curHeight != h || curWidth != w){
3336                resizeEvent.fire(curWidth = w, curHeight = h);
3337            }
3338        },
3339
3340        /**
3341         * Adds a listener to be notified when the browser window is resized and provides resize event buffering (50 milliseconds),
3342         * passes new viewport width and height to handlers.
3343         * @param {Function} fn      The handler function the window resize event invokes.
3344         * @param {Object}   scope   The scope (<code>this</code> reference) in which the handler function executes. Defaults to the browser window.
3345         * @param {boolean}  options Options object as passed to {@link Ext.Element#addListener}
3346         */
3347        onWindowResize : function(fn, scope, options){
3348            if(!resizeEvent){
3349                resizeEvent = new Ext.util.Event();
3350                resizeTask = new Ext.util.DelayedTask(this.doResizeEvent);
3351                Ext.EventManager.on(window, "resize", this.fireWindowResize, this);
3352            }
3353            resizeEvent.addListener(fn, scope, options);
3354        },
3355
3356        // exposed only to allow manual firing
3357        fireWindowResize : function(){
3358            if(resizeEvent){
3359                if((Ext.isIE||Ext.isAir) && resizeTask){
3360                    resizeTask.delay(50);
3361                }else{
3362                    resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
3363                }
3364            }
3365        },
3366
3367        /**
3368         * 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.
3369         * @param {Function} fn      The function the event invokes.
3370         * @param {Object}   scope   The scope (<code>this</code> reference) in which the handler function executes. Defaults to the browser window.
3371         * @param {boolean}  options Options object as passed to {@link Ext.Element#addListener}
3372         */
3373        onTextResize : function(fn, scope, options){
3374            if(!textEvent){
3375                textEvent = new Ext.util.Event();
3376                var textEl = new Ext.Element(document.createElement('div'));
3377                textEl.dom.className = 'x-text-resize';
3378                textEl.dom.innerHTML = 'X';
3379                textEl.appendTo(document.body);
3380                textSize = textEl.dom.offsetHeight;
3381                setInterval(function(){
3382                    if(textEl.dom.offsetHeight != textSize){
3383                        textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
3384                    }
3385                }, this.textResizeInterval);
3386            }
3387            textEvent.addListener(fn, scope, options);
3388        },
3389
3390        /**
3391         * Removes the passed window resize listener.
3392         * @param {Function} fn        The method the event invokes
3393         * @param {Object}   scope    The scope of handler
3394         */
3395        removeResizeListener : function(fn, scope){
3396            if(resizeEvent){
3397                resizeEvent.removeListener(fn, scope);
3398            }
3399        },
3400
3401        // private
3402        fireResize : function(){
3403            if(resizeEvent){
3404                resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
3405            }
3406        },
3407
3408         /**
3409         * The frequency, in milliseconds, to check for text resize events (defaults to 50)
3410         */
3411        textResizeInterval : 50,
3412
3413        /**
3414         * Url used for onDocumentReady with using SSL (defaults to Ext.SSL_SECURE_URL)
3415         */
3416        ieDeferSrc : false,
3417
3418        // protected for use inside the framework
3419        // detects whether we should use keydown or keypress based on the browser.
3420        useKeydown: useKeydown
3421    };
3422 }());
3423
3424 Ext.EventManager.on = Ext.EventManager.addListener;
3425
3426
3427 Ext.apply(Ext.EventObjectImpl.prototype, {
3428    /** Key constant @type Number */
3429    BACKSPACE: 8,
3430    /** Key constant @type Number */
3431    TAB: 9,
3432    /** Key constant @type Number */
3433    NUM_CENTER: 12,
3434    /** Key constant @type Number */
3435    ENTER: 13,
3436    /** Key constant @type Number */
3437    RETURN: 13,
3438    /** Key constant @type Number */
3439    SHIFT: 16,
3440    /** Key constant @type Number */
3441    CTRL: 17,
3442    CONTROL : 17, // legacy
3443    /** Key constant @type Number */
3444    ALT: 18,
3445    /** Key constant @type Number */
3446    PAUSE: 19,
3447    /** Key constant @type Number */
3448    CAPS_LOCK: 20,
3449    /** Key constant @type Number */
3450    ESC: 27,
3451    /** Key constant @type Number */
3452    SPACE: 32,
3453    /** Key constant @type Number */
3454    PAGE_UP: 33,
3455    PAGEUP : 33, // legacy
3456    /** Key constant @type Number */
3457    PAGE_DOWN: 34,
3458    PAGEDOWN : 34, // legacy
3459    /** Key constant @type Number */
3460    END: 35,
3461    /** Key constant @type Number */
3462    HOME: 36,
3463    /** Key constant @type Number */
3464    LEFT: 37,
3465    /** Key constant @type Number */
3466    UP: 38,
3467    /** Key constant @type Number */
3468    RIGHT: 39,
3469    /** Key constant @type Number */
3470    DOWN: 40,
3471    /** Key constant @type Number */
3472    PRINT_SCREEN: 44,
3473    /** Key constant @type Number */
3474    INSERT: 45,
3475    /** Key constant @type Number */
3476    DELETE: 46,
3477    /** Key constant @type Number */
3478    ZERO: 48,
3479    /** Key constant @type Number */
3480    ONE: 49,
3481    /** Key constant @type Number */
3482    TWO: 50,
3483    /** Key constant @type Number */
3484    THREE: 51,
3485    /** Key constant @type Number */
3486    FOUR: 52,
3487    /** Key constant @type Number */
3488    FIVE: 53,
3489    /** Key constant @type Number */
3490    SIX: 54,
3491    /** Key constant @type Number */
3492    SEVEN: 55,
3493    /** Key constant @type Number */
3494    EIGHT: 56,
3495    /** Key constant @type Number */
3496    NINE: 57,
3497    /** Key constant @type Number */
3498    A: 65,
3499    /** Key constant @type Number */
3500    B: 66,
3501    /** Key constant @type Number */
3502    C: 67,
3503    /** Key constant @type Number */
3504    D: 68,
3505    /** Key constant @type Number */
3506    E: 69,
3507    /** Key constant @type Number */
3508    F: 70,
3509    /** Key constant @type Number */
3510    G: 71,
3511    /** Key constant @type Number */
3512    H: 72,
3513    /** Key constant @type Number */
3514    I: 73,
3515    /** Key constant @type Number */
3516    J: 74,
3517    /** Key constant @type Number */
3518    K: 75,
3519    /** Key constant @type Number */
3520    L: 76,
3521    /** Key constant @type Number */
3522    M: 77,
3523    /** Key constant @type Number */
3524    N: 78,
3525    /** Key constant @type Number */
3526    O: 79,
3527    /** Key constant @type Number */
3528    P: 80,
3529    /** Key constant @type Number */
3530    Q: 81,
3531    /** Key constant @type Number */
3532    R: 82,
3533    /** Key constant @type Number */
3534    S: 83,
3535    /** Key constant @type Number */
3536    T: 84,
3537    /** Key constant @type Number */
3538    U: 85,
3539    /** Key constant @type Number */
3540    V: 86,
3541    /** Key constant @type Number */
3542    W: 87,
3543    /** Key constant @type Number */
3544    X: 88,
3545    /** Key constant @type Number */
3546    Y: 89,
3547    /** Key constant @type Number */
3548    Z: 90,
3549    /** Key constant @type Number */
3550    CONTEXT_MENU: 93,
3551    /** Key constant @type Number */
3552    NUM_ZERO: 96,
3553    /** Key constant @type Number */
3554    NUM_ONE: 97,
3555    /** Key constant @type Number */
3556    NUM_TWO: 98,
3557    /** Key constant @type Number */
3558    NUM_THREE: 99,
3559    /** Key constant @type Number */
3560    NUM_FOUR: 100,
3561    /** Key constant @type Number */
3562    NUM_FIVE: 101,
3563    /** Key constant @type Number */
3564    NUM_SIX: 102,
3565    /** Key constant @type Number */
3566    NUM_SEVEN: 103,
3567    /** Key constant @type Number */
3568    NUM_EIGHT: 104,
3569    /** Key constant @type Number */
3570    NUM_NINE: 105,
3571    /** Key constant @type Number */
3572    NUM_MULTIPLY: 106,
3573    /** Key constant @type Number */
3574    NUM_PLUS: 107,
3575    /** Key constant @type Number */
3576    NUM_MINUS: 109,
3577    /** Key constant @type Number */
3578    NUM_PERIOD: 110,
3579    /** Key constant @type Number */
3580    NUM_DIVISION: 111,
3581    /** Key constant @type Number */
3582    F1: 112,
3583    /** Key constant @type Number */
3584    F2: 113,
3585    /** Key constant @type Number */
3586    F3: 114,
3587    /** Key constant @type Number */
3588    F4: 115,
3589    /** Key constant @type Number */
3590    F5: 116,
3591    /** Key constant @type Number */
3592    F6: 117,
3593    /** Key constant @type Number */
3594    F7: 118,
3595    /** Key constant @type Number */
3596    F8: 119,
3597    /** Key constant @type Number */
3598    F9: 120,
3599    /** Key constant @type Number */
3600    F10: 121,
3601    /** Key constant @type Number */
3602    F11: 122,
3603    /** Key constant @type Number */
3604    F12: 123,
3605
3606    /** @private */
3607    isNavKeyPress : function(){
3608        var me = this,
3609            k = this.normalizeKey(me.keyCode);
3610        return (k >= 33 && k <= 40) ||  // Page Up/Down, End, Home, Left, Up, Right, Down
3611        k == me.RETURN ||
3612        k == me.TAB ||
3613        k == me.ESC;
3614    },
3615
3616    isSpecialKey : function(){
3617        var k = this.normalizeKey(this.keyCode);
3618        return (this.type == 'keypress' && this.ctrlKey) ||
3619        this.isNavKeyPress() ||
3620        (k == this.BACKSPACE) || // Backspace
3621        (k >= 16 && k <= 20) || // Shift, Ctrl, Alt, Pause, Caps Lock
3622        (k >= 44 && k <= 45);   // Print Screen, Insert
3623    },
3624
3625    getPoint : function(){
3626        return new Ext.lib.Point(this.xy[0], this.xy[1]);
3627    },
3628
3629    /**
3630     * Returns true if the control, meta, shift or alt key was pressed during this event.
3631     * @return {Boolean}
3632     */
3633    hasModifier : function(){
3634        return ((this.ctrlKey || this.altKey) || this.shiftKey);
3635    }
3636 });/**
3637  * @class Ext.Element
3638  * <p>Encapsulates a DOM element, adding simple DOM manipulation facilities, normalizing for browser differences.</p>
3639  * <p>All instances of this class inherit the methods of {@link Ext.Fx} making visual effects easily available to all DOM elements.</p>
3640  * <p>Note that the events documented in this class are not Ext events, they encapsulate browser events. To
3641  * access the underlying browser event, see {@link Ext.EventObject#browserEvent}. Some older
3642  * browsers may not support the full range of events. Which events are supported is beyond the control of ExtJs.</p>
3643  * Usage:<br>
3644 <pre><code>
3645 // by id
3646 var el = Ext.get("my-div");
3647
3648 // by DOM element reference
3649 var el = Ext.get(myDivElement);
3650 </code></pre>
3651  * <b>Animations</b><br />
3652  * <p>When an element is manipulated, by default there is no animation.</p>
3653  * <pre><code>
3654 var el = Ext.get("my-div");
3655
3656 // no animation
3657 el.setWidth(100);
3658  * </code></pre>
3659  * <p>Many of the functions for manipulating an element have an optional "animate" parameter.  This
3660  * parameter can be specified as boolean (<tt>true</tt>) for default animation effects.</p>
3661  * <pre><code>
3662 // default animation
3663 el.setWidth(100, true);
3664  * </code></pre>
3665  *
3666  * <p>To configure the effects, an object literal with animation options to use as the Element animation
3667  * configuration object can also be specified. Note that the supported Element animation configuration
3668  * options are a subset of the {@link Ext.Fx} animation options specific to Fx effects.  The supported
3669  * Element animation configuration options are:</p>
3670 <pre>
3671 Option    Default   Description
3672 --------- --------  ---------------------------------------------
3673 {@link Ext.Fx#duration duration}  .35       The duration of the animation in seconds
3674 {@link Ext.Fx#easing easing}    easeOut   The easing method
3675 {@link Ext.Fx#callback callback}  none      A function to execute when the anim completes
3676 {@link Ext.Fx#scope scope}     this      The scope (this) of the callback function
3677 </pre>
3678  *
3679  * <pre><code>
3680 // Element animation options object
3681 var opt = {
3682     {@link Ext.Fx#duration duration}: 1,
3683     {@link Ext.Fx#easing easing}: 'elasticIn',
3684     {@link Ext.Fx#callback callback}: this.foo,
3685     {@link Ext.Fx#scope scope}: this
3686 };
3687 // animation with some options set
3688 el.setWidth(100, opt);
3689  * </code></pre>
3690  * <p>The Element animation object being used for the animation will be set on the options
3691  * object as "anim", which allows you to stop or manipulate the animation. Here is an example:</p>
3692  * <pre><code>
3693 // using the "anim" property to get the Anim object
3694 if(opt.anim.isAnimated()){
3695     opt.anim.stop();
3696 }
3697  * </code></pre>
3698  * <p>Also see the <tt>{@link #animate}</tt> method for another animation technique.</p>
3699  * <p><b> Composite (Collections of) Elements</b></p>
3700  * <p>For working with collections of Elements, see {@link Ext.CompositeElement}</p>
3701  * @constructor Create a new Element directly.
3702  * @param {String/HTMLElement} element
3703  * @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).
3704  */
3705 (function(){
3706 var DOC = document;
3707
3708 Ext.Element = function(element, forceNew){
3709     var dom = typeof element == "string" ?
3710               DOC.getElementById(element) : element,
3711         id;
3712
3713     if(!dom) return null;
3714
3715     id = dom.id;
3716
3717     if(!forceNew && id && Ext.elCache[id]){ // element object already exists
3718         return Ext.elCache[id].el;
3719     }
3720
3721     /**
3722      * The DOM element
3723      * @type HTMLElement
3724      */
3725     this.dom = dom;
3726
3727     /**
3728      * The DOM element ID
3729      * @type String
3730      */
3731     this.id = id || Ext.id(dom);
3732 };
3733
3734 var D = Ext.lib.Dom,
3735     DH = Ext.DomHelper,
3736     E = Ext.lib.Event,
3737     A = Ext.lib.Anim,
3738     El = Ext.Element,
3739     EC = Ext.elCache;
3740
3741 El.prototype = {
3742     /**
3743      * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
3744      * @param {Object} o The object with the attributes
3745      * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
3746      * @return {Ext.Element} this
3747      */
3748     set : function(o, useSet){
3749         var el = this.dom,
3750             attr,
3751             val,
3752             useSet = (useSet !== false) && !!el.setAttribute;
3753
3754         for(attr in o){
3755             if (o.hasOwnProperty(attr)) {
3756                 val = o[attr];
3757                 if (attr == 'style') {
3758                     DH.applyStyles(el, val);
3759                 } else if (attr == 'cls') {
3760                     el.className = val;
3761                 } else if (useSet) {
3762                     el.setAttribute(attr, val);
3763                 } else {
3764                     el[attr] = val;
3765                 }
3766             }
3767         }
3768         return this;
3769     },
3770
3771 //  Mouse events
3772     /**
3773      * @event click
3774      * Fires when a mouse click is detected within the element.
3775      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
3776      * @param {HtmlElement} t The target of the event.
3777      * @param {Object} o The options configuration passed to the {@link #addListener} call.
3778      */
3779     /**
3780      * @event contextmenu
3781      * Fires when a right click is detected within the element.
3782      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
3783      * @param {HtmlElement} t The target of the event.
3784      * @param {Object} o The options configuration passed to the {@link #addListener} call.
3785      */
3786     /**
3787      * @event dblclick
3788      * Fires when a mouse double click is detected within the element.
3789      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
3790      * @param {HtmlElement} t The target of the event.
3791      * @param {Object} o The options configuration passed to the {@link #addListener} call.
3792      */
3793     /**
3794      * @event mousedown
3795      * Fires when a mousedown is detected within the element.
3796      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
3797      * @param {HtmlElement} t The target of the event.
3798      * @param {Object} o The options configuration passed to the {@link #addListener} call.
3799      */
3800     /**
3801      * @event mouseup
3802      * Fires when a mouseup is detected within the element.
3803      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
3804      * @param {HtmlElement} t The target of the event.
3805      * @param {Object} o The options configuration passed to the {@link #addListener} call.
3806      */
3807     /**
3808      * @event mouseover
3809      * Fires when a mouseover is detected within the element.
3810      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
3811      * @param {HtmlElement} t The target of the event.
3812      * @param {Object} o The options configuration passed to the {@link #addListener} call.
3813      */
3814     /**
3815      * @event mousemove
3816      * Fires when a mousemove is detected with the element.
3817      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
3818      * @param {HtmlElement} t The target of the event.
3819      * @param {Object} o The options configuration passed to the {@link #addListener} call.
3820      */
3821     /**
3822      * @event mouseout
3823      * Fires when a mouseout is detected with the element.
3824      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
3825      * @param {HtmlElement} t The target of the event.
3826      * @param {Object} o The options configuration passed to the {@link #addListener} call.
3827      */
3828     /**
3829      * @event mouseenter
3830      * Fires when the mouse enters the element.
3831      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
3832      * @param {HtmlElement} t The target of the event.
3833      * @param {Object} o The options configuration passed to the {@link #addListener} call.
3834      */
3835     /**
3836      * @event mouseleave
3837      * Fires when the mouse leaves the element.
3838      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
3839      * @param {HtmlElement} t The target of the event.
3840      * @param {Object} o The options configuration passed to the {@link #addListener} call.
3841      */
3842
3843 //  Keyboard events
3844     /**
3845      * @event keypress
3846      * Fires when a keypress is detected within the element.
3847      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
3848      * @param {HtmlElement} t The target of the event.
3849      * @param {Object} o The options configuration passed to the {@link #addListener} call.
3850      */
3851     /**
3852      * @event keydown
3853      * Fires when a keydown is detected within the element.
3854      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
3855      * @param {HtmlElement} t The target of the event.
3856      * @param {Object} o The options configuration passed to the {@link #addListener} call.
3857      */
3858     /**
3859      * @event keyup
3860      * Fires when a keyup is detected within the element.
3861      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
3862      * @param {HtmlElement} t The target of the event.
3863      * @param {Object} o The options configuration passed to the {@link #addListener} call.
3864      */
3865
3866
3867 //  HTML frame/object events
3868     /**
3869      * @event load
3870      * Fires when the user agent finishes loading all content within the element. Only supported by window, frames, objects and images.
3871      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
3872      * @param {HtmlElement} t The target of the event.
3873      * @param {Object} o The options configuration passed to the {@link #addListener} call.
3874      */
3875     /**
3876      * @event unload
3877      * 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.
3878      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
3879      * @param {HtmlElement} t The target of the event.
3880      * @param {Object} o The options configuration passed to the {@link #addListener} call.
3881      */
3882     /**
3883      * @event abort
3884      * Fires when an object/image is stopped from loading before completely loaded.
3885      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
3886      * @param {HtmlElement} t The target of the event.
3887      * @param {Object} o The options configuration passed to the {@link #addListener} call.
3888      */
3889     /**
3890      * @event error
3891      * Fires when an object/image/frame cannot be loaded properly.
3892      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
3893      * @param {HtmlElement} t The target of the event.
3894      * @param {Object} o The options configuration passed to the {@link #addListener} call.
3895      */
3896     /**
3897      * @event resize
3898      * Fires when a document view is resized.
3899      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
3900      * @param {HtmlElement} t The target of the event.
3901      * @param {Object} o The options configuration passed to the {@link #addListener} call.
3902      */
3903     /**
3904      * @event scroll
3905      * Fires when a document view is scrolled.
3906      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
3907      * @param {HtmlElement} t The target of the event.
3908      * @param {Object} o The options configuration passed to the {@link #addListener} call.
3909      */
3910
3911 //  Form events
3912     /**
3913      * @event select
3914      * Fires when a user selects some text in a text field, including input and textarea.
3915      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
3916      * @param {HtmlElement} t The target of the event.
3917      * @param {Object} o The options configuration passed to the {@link #addListener} call.
3918      */
3919     /**
3920      * @event change
3921      * Fires when a control loses the input focus and its value has been modified since gaining focus.
3922      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
3923      * @param {HtmlElement} t The target of the event.
3924      * @param {Object} o The options configuration passed to the {@link #addListener} call.
3925      */
3926     /**
3927      * @event submit
3928      * Fires when a form is submitted.
3929      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
3930      * @param {HtmlElement} t The target of the event.
3931      * @param {Object} o The options configuration passed to the {@link #addListener} call.
3932      */
3933     /**
3934      * @event reset
3935      * Fires when a form is reset.
3936      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
3937      * @param {HtmlElement} t The target of the event.
3938      * @param {Object} o The options configuration passed to the {@link #addListener} call.
3939      */
3940     /**
3941      * @event focus
3942      * Fires when an element receives focus either via the pointing device or by tab navigation.
3943      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
3944      * @param {HtmlElement} t The target of the event.
3945      * @param {Object} o The options configuration passed to the {@link #addListener} call.
3946      */
3947     /**
3948      * @event blur
3949      * Fires when an element loses focus either via the pointing device or by tabbing navigation.
3950      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
3951      * @param {HtmlElement} t The target of the event.
3952      * @param {Object} o The options configuration passed to the {@link #addListener} call.
3953      */
3954
3955 //  User Interface events
3956     /**
3957      * @event DOMFocusIn
3958      * Where supported. Similar to HTML focus event, but can be applied to any focusable element.
3959      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
3960      * @param {HtmlElement} t The target of the event.
3961      * @param {Object} o The options configuration passed to the {@link #addListener} call.
3962      */
3963     /**
3964      * @event DOMFocusOut
3965      * Where supported. Similar to HTML blur event, but can be applied to any focusable element.
3966      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
3967      * @param {HtmlElement} t The target of the event.
3968      * @param {Object} o The options configuration passed to the {@link #addListener} call.
3969      */
3970     /**
3971      * @event DOMActivate
3972      * Where supported. Fires when an element is activated, for instance, through a mouse click or a keypress.
3973      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
3974      * @param {HtmlElement} t The target of the event.
3975      * @param {Object} o The options configuration passed to the {@link #addListener} call.
3976      */
3977
3978 //  DOM Mutation events
3979     /**
3980      * @event DOMSubtreeModified
3981      * Where supported. Fires when the subtree is modified.
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 DOMNodeInserted
3988      * Where supported. Fires when a node has been added as a child of another node.
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 DOMNodeRemoved
3995      * Where supported. Fires when a descendant node of the element is removed.
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 DOMNodeRemovedFromDocument
4002      * Where supported. Fires when a node is being removed from a document.
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 DOMNodeInsertedIntoDocument
4009      * Where supported. Fires when a node is being inserted into a document.
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      * @event DOMAttrModified
4016      * Where supported. Fires when an attribute has been modified.
4017      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4018      * @param {HtmlElement} t The target of the event.
4019      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4020      */
4021     /**
4022      * @event DOMCharacterDataModified
4023      * Where supported. Fires when the character data has been modified.
4024      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4025      * @param {HtmlElement} t The target of the event.
4026      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4027      */
4028
4029     /**
4030      * The default unit to append to CSS values where a unit isn't provided (defaults to px).
4031      * @type String
4032      */
4033     defaultUnit : "px",
4034
4035     /**
4036      * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
4037      * @param {String} selector The simple selector to test
4038      * @return {Boolean} True if this element matches the selector, else false
4039      */
4040     is : function(simpleSelector){
4041         return Ext.DomQuery.is(this.dom, simpleSelector);
4042     },
4043
4044     /**
4045      * Tries to focus the element. Any exceptions are caught and ignored.
4046      * @param {Number} defer (optional) Milliseconds to defer the focus
4047      * @return {Ext.Element} this
4048      */
4049     focus : function(defer, /* private */ dom) {
4050         var me = this,
4051             dom = dom || me.dom;
4052         try{
4053             if(Number(defer)){
4054                 me.focus.defer(defer, null, [null, dom]);
4055             }else{
4056                 dom.focus();
4057             }
4058         }catch(e){}
4059         return me;
4060     },
4061
4062     /**
4063      * Tries to blur the element. Any exceptions are caught and ignored.
4064      * @return {Ext.Element} this
4065      */
4066     blur : function() {
4067         try{
4068             this.dom.blur();
4069         }catch(e){}
4070         return this;
4071     },
4072
4073     /**
4074      * Returns the value of the "value" attribute
4075      * @param {Boolean} asNumber true to parse the value as a number
4076      * @return {String/Number}
4077      */
4078     getValue : function(asNumber){
4079         var val = this.dom.value;
4080         return asNumber ? parseInt(val, 10) : val;
4081     },
4082
4083     /**
4084      * Appends an event handler to this element.  The shorthand version {@link #on} is equivalent.
4085      * @param {String} eventName The name of event to handle.
4086      * @param {Function} fn The handler function the event invokes. This function is passed
4087      * the following parameters:<ul>
4088      * <li><b>evt</b> : EventObject<div class="sub-desc">The {@link Ext.EventObject EventObject} describing the event.</div></li>
4089      * <li><b>el</b> : HtmlElement<div class="sub-desc">The DOM element which was the target of the event.
4090      * Note that this may be filtered by using the <tt>delegate</tt> option.</div></li>
4091      * <li><b>o</b> : Object<div class="sub-desc">The options object from the addListener call.</div></li>
4092      * </ul>
4093      * @param {Object} scope (optional) The scope (<code><b>this</b></code> reference) in which the handler function is executed.
4094      * <b>If omitted, defaults to this Element.</b>.
4095      * @param {Object} options (optional) An object containing handler configuration properties.
4096      * This may contain any of the following properties:<ul>
4097      * <li><b>scope</b> Object : <div class="sub-desc">The scope (<code><b>this</b></code> reference) in which the handler function is executed.
4098      * <b>If omitted, defaults to this Element.</b></div></li>
4099      * <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>
4100      * <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>
4101      * <li><b>preventDefault</b> Boolean: <div class="sub-desc">True to prevent the default action</div></li>
4102      * <li><b>stopPropagation</b> Boolean: <div class="sub-desc">True to prevent event propagation</div></li>
4103      * <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>
4104      * <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>
4105      * <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>
4106      * <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>
4107      * <li><b>buffer</b> Number: <div class="sub-desc">Causes the handler to be scheduled to run in an {@link Ext.util.DelayedTask} delayed
4108      * by the specified number of milliseconds. If the event fires again within that time, the original
4109      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</div></li>
4110      * </ul><br>
4111      * <p>
4112      * <b>Combining Options</b><br>
4113      * In the following examples, the shorthand form {@link #on} is used rather than the more verbose
4114      * addListener.  The two are equivalent.  Using the options argument, it is possible to combine different
4115      * types of listeners:<br>
4116      * <br>
4117      * A delayed, one-time listener that auto stops the event and adds a custom argument (forumId) to the
4118      * options object. The options object is available as the third parameter in the handler function.<div style="margin: 5px 20px 20px;">
4119      * Code:<pre><code>
4120 el.on('click', this.onClick, this, {
4121     single: true,
4122     delay: 100,
4123     stopEvent : true,
4124     forumId: 4
4125 });</code></pre></p>
4126      * <p>
4127      * <b>Attaching multiple handlers in 1 call</b><br>
4128      * The method also allows for a single argument to be passed which is a config object containing properties
4129      * which specify multiple handlers.</p>
4130      * <p>
4131      * Code:<pre><code>
4132 el.on({
4133     'click' : {
4134         fn: this.onClick,
4135         scope: this,
4136         delay: 100
4137     },
4138     'mouseover' : {
4139         fn: this.onMouseOver,
4140         scope: this
4141     },
4142     'mouseout' : {
4143         fn: this.onMouseOut,
4144         scope: this
4145     }
4146 });</code></pre>
4147      * <p>
4148      * Or a shorthand syntax:<br>
4149      * Code:<pre><code></p>
4150 el.on({
4151     'click' : this.onClick,
4152     'mouseover' : this.onMouseOver,
4153     'mouseout' : this.onMouseOut,
4154     scope: this
4155 });
4156      * </code></pre></p>
4157      * <p><b>delegate</b></p>
4158      * <p>This is a configuration option that you can pass along when registering a handler for
4159      * an event to assist with event delegation. Event delegation is a technique that is used to
4160      * reduce memory consumption and prevent exposure to memory-leaks. By registering an event
4161      * for a container element as opposed to each element within a container. By setting this
4162      * configuration option to a simple selector, the target element will be filtered to look for
4163      * a descendant of the target.
4164      * For example:<pre><code>
4165 // using this markup:
4166 &lt;div id='elId'>
4167     &lt;p id='p1'>paragraph one&lt;/p>
4168     &lt;p id='p2' class='clickable'>paragraph two&lt;/p>
4169     &lt;p id='p3'>paragraph three&lt;/p>
4170 &lt;/div>
4171 // utilize event delegation to registering just one handler on the container element:
4172 el = Ext.get('elId');
4173 el.on(
4174     'click',
4175     function(e,t) {
4176         // handle click
4177         console.info(t.id); // 'p2'
4178     },
4179     this,
4180     {
4181         // filter the target element to be a descendant with the class 'clickable'
4182         delegate: '.clickable'
4183     }
4184 );
4185      * </code></pre></p>
4186      * @return {Ext.Element} this
4187      */
4188     addListener : function(eventName, fn, scope, options){
4189         Ext.EventManager.on(this.dom,  eventName, fn, scope || this, options);
4190         return this;
4191     },
4192
4193     /**
4194      * Removes an event handler from this element.  The shorthand version {@link #un} is equivalent.
4195      * <b>Note</b>: if a <i>scope</i> was explicitly specified when {@link #addListener adding} the
4196      * listener, the same scope must be specified here.
4197      * Example:
4198      * <pre><code>
4199 el.removeListener('click', this.handlerFn);
4200 // or
4201 el.un('click', this.handlerFn);
4202 </code></pre>
4203      * @param {String} eventName The name of the event from which to remove the handler.
4204      * @param {Function} fn The handler function to remove. <b>This must be a reference to the function passed into the {@link #addListener} call.</b>
4205      * @param {Object} scope If a scope (<b><code>this</code></b> reference) was specified when the listener was added,
4206      * then this must refer to the same object.
4207      * @return {Ext.Element} this
4208      */
4209     removeListener : function(eventName, fn, scope){
4210         Ext.EventManager.removeListener(this.dom,  eventName, fn, scope || this);
4211         return this;
4212     },
4213
4214     /**
4215      * Removes all previous added listeners from this element
4216      * @return {Ext.Element} this
4217      */
4218     removeAllListeners : function(){
4219         Ext.EventManager.removeAll(this.dom);
4220         return this;
4221     },
4222
4223     /**
4224      * Recursively removes all previous added listeners from this element and its children
4225      * @return {Ext.Element} this
4226      */
4227     purgeAllListeners : function() {
4228         Ext.EventManager.purgeElement(this, true);
4229         return this;
4230     },
4231     /**
4232      * @private Test if size has a unit, otherwise appends the default
4233      */
4234     addUnits : function(size){
4235         if(size === "" || size == "auto" || size === undefined){
4236             size = size || '';
4237         } else if(!isNaN(size) || !unitPattern.test(size)){
4238             size = size + (this.defaultUnit || 'px');
4239         }
4240         return size;
4241     },
4242
4243     /**
4244      * <p>Updates the <a href="http://developer.mozilla.org/en/DOM/element.innerHTML">innerHTML</a> of this Element
4245      * 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>
4246      * <p>Updating innerHTML of an element will <b>not</b> execute embedded <tt>&lt;script></tt> elements. This is a browser restriction.</p>
4247      * @param {Mixed} options. Either a sring containing the URL from which to load the HTML, or an {@link Ext.Ajax#request} options object specifying
4248      * exactly how to request the HTML.
4249      * @return {Ext.Element} this
4250      */
4251     load : function(url, params, cb){
4252         Ext.Ajax.request(Ext.apply({
4253             params: params,
4254             url: url.url || url,
4255             callback: cb,
4256             el: this.dom,
4257             indicatorText: url.indicatorText || ''
4258         }, Ext.isObject(url) ? url : {}));
4259         return this;
4260     },
4261
4262     /**
4263      * Tests various css rules/browsers to determine if this element uses a border box
4264      * @return {Boolean}
4265      */
4266     isBorderBox : function(){
4267         return noBoxAdjust[(this.dom.tagName || "").toLowerCase()] || Ext.isBorderBox;
4268     },
4269
4270     /**
4271      * <p>Removes this element's dom reference.  Note that event and cache removal is handled at {@link Ext#removeNode}</p>
4272      */
4273     remove : function(){
4274         var me = this,
4275             dom = me.dom;
4276
4277         if (dom) {
4278             delete me.dom;
4279             Ext.removeNode(dom);
4280         }
4281     },
4282
4283     /**
4284      * Sets up event handlers to call the passed functions when the mouse is moved into and out of the Element.
4285      * @param {Function} overFn The function to call when the mouse enters the Element.
4286      * @param {Function} outFn The function to call when the mouse leaves the Element.
4287      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the functions are executed. Defaults to the Element's DOM element.
4288      * @param {Object} options (optional) Options for the listener. See {@link Ext.util.Observable#addListener the <tt>options</tt> parameter}.
4289      * @return {Ext.Element} this
4290      */
4291     hover : function(overFn, outFn, scope, options){
4292         var me = this;
4293         me.on('mouseenter', overFn, scope || me.dom, options);
4294         me.on('mouseleave', outFn, scope || me.dom, options);
4295         return me;
4296     },
4297
4298     /**
4299      * Returns true if this element is an ancestor of the passed element
4300      * @param {HTMLElement/String} el The element to check
4301      * @return {Boolean} True if this element is an ancestor of el, else false
4302      */
4303     contains : function(el){
4304         return !el ? false : Ext.lib.Dom.isAncestor(this.dom, el.dom ? el.dom : el);
4305     },
4306
4307     /**
4308      * Returns the value of a namespaced attribute from the element's underlying DOM node.
4309      * @param {String} namespace The namespace in which to look for the attribute
4310      * @param {String} name The attribute name
4311      * @return {String} The attribute value
4312      * @deprecated
4313      */
4314     getAttributeNS : function(ns, name){
4315         return this.getAttribute(name, ns);
4316     },
4317
4318     /**
4319      * Returns the value of an attribute from the element's underlying DOM node.
4320      * @param {String} name The attribute name
4321      * @param {String} namespace (optional) The namespace in which to look for the attribute
4322      * @return {String} The attribute value
4323      */
4324     getAttribute : Ext.isIE ? function(name, ns){
4325         var d = this.dom,
4326             type = typeof d[ns + ":" + name];
4327
4328         if(['undefined', 'unknown'].indexOf(type) == -1){
4329             return d[ns + ":" + name];
4330         }
4331         return d[name];
4332     } : function(name, ns){
4333         var d = this.dom;
4334         return d.getAttributeNS(ns, name) || d.getAttribute(ns + ":" + name) || d.getAttribute(name) || d[name];
4335     },
4336
4337     /**
4338     * Update the innerHTML of this element
4339     * @param {String} html The new HTML
4340     * @return {Ext.Element} this
4341      */
4342     update : function(html) {
4343         if (this.dom) {
4344             this.dom.innerHTML = html;
4345         }
4346         return this;
4347     }
4348 };
4349
4350 var ep = El.prototype;
4351
4352 El.addMethods = function(o){
4353    Ext.apply(ep, o);
4354 };
4355
4356 /**
4357  * Appends an event handler (shorthand for {@link #addListener}).
4358  * @param {String} eventName The name of event to handle.
4359  * @param {Function} fn The handler function the event invokes.
4360  * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the handler function is executed.
4361  * @param {Object} options (optional) An object containing standard {@link #addListener} options
4362  * @member Ext.Element
4363  * @method on
4364  */
4365 ep.on = ep.addListener;
4366
4367 /**
4368  * Removes an event handler from this element (see {@link #removeListener} for additional notes).
4369  * @param {String} eventName The name of the event from which to remove the handler.
4370  * @param {Function} fn The handler function to remove. <b>This must be a reference to the function passed into the {@link #addListener} call.</b>
4371  * @param {Object} scope If a scope (<b><code>this</code></b> reference) was specified when the listener was added,
4372  * then this must refer to the same object.
4373  * @return {Ext.Element} this
4374  * @member Ext.Element
4375  * @method un
4376  */
4377 ep.un = ep.removeListener;
4378
4379 /**
4380  * true to automatically adjust width and height settings for box-model issues (default to true)
4381  */
4382 ep.autoBoxAdjust = true;
4383
4384 // private
4385 var unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i,
4386     docEl;
4387
4388 /**
4389  * @private
4390  */
4391
4392 /**
4393  * Retrieves Ext.Element objects.
4394  * <p><b>This method does not retrieve {@link Ext.Component Component}s.</b> This method
4395  * retrieves Ext.Element objects which encapsulate DOM elements. To retrieve a Component by
4396  * its ID, use {@link Ext.ComponentMgr#get}.</p>
4397  * <p>Uses simple caching to consistently return the same object. Automatically fixes if an
4398  * object was recreated with the same id via AJAX or DOM.</p>
4399  * @param {Mixed} el The id of the node, a DOM Node or an existing Element.
4400  * @return {Element} The Element object (or null if no matching element was found)
4401  * @static
4402  * @member Ext.Element
4403  * @method get
4404  */
4405 El.get = function(el){
4406     var ex,
4407         elm,
4408         id;
4409     if(!el){ return null; }
4410     if (typeof el == "string") { // element id
4411         if (!(elm = DOC.getElementById(el))) {
4412             return null;
4413         }
4414         if (EC[el] && EC[el].el) {
4415             ex = EC[el].el;
4416             ex.dom = elm;
4417         } else {
4418             ex = El.addToCache(new El(elm));
4419         }
4420         return ex;
4421     } else if (el.tagName) { // dom element
4422         if(!(id = el.id)){
4423             id = Ext.id(el);
4424         }
4425         if (EC[id] && EC[id].el) {
4426             ex = EC[id].el;
4427             ex.dom = el;
4428         } else {
4429             ex = El.addToCache(new El(el));
4430         }
4431         return ex;
4432     } else if (el instanceof El) {
4433         if(el != docEl){
4434             el.dom = DOC.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
4435                                                           // catch case where it hasn't been appended
4436         }
4437         return el;
4438     } else if(el.isComposite) {
4439         return el;
4440     } else if(Ext.isArray(el)) {
4441         return El.select(el);
4442     } else if(el == DOC) {
4443         // create a bogus element object representing the document object
4444         if(!docEl){
4445             var f = function(){};
4446             f.prototype = El.prototype;
4447             docEl = new f();
4448             docEl.dom = DOC;
4449         }
4450         return docEl;
4451     }
4452     return null;
4453 };
4454
4455 El.addToCache = function(el, id){
4456     id = id || el.id;    
4457     EC[id] = {
4458         el:  el,
4459         data: {},
4460         events: {}
4461     };
4462     return el;
4463 };
4464
4465 // private method for getting and setting element data
4466 El.data = function(el, key, value){
4467     el = El.get(el);
4468     if (!el) {
4469         return null;
4470     }
4471     var c = EC[el.id].data;
4472     if(arguments.length == 2){
4473         return c[key];
4474     }else{
4475         return (c[key] = value);
4476     }
4477 };
4478
4479 // private
4480 // Garbage collection - uncache elements/purge listeners on orphaned elements
4481 // so we don't hold a reference and cause the browser to retain them
4482 function garbageCollect(){
4483     if(!Ext.enableGarbageCollector){
4484         clearInterval(El.collectorThreadId);
4485     } else {
4486         var eid,
4487             el,
4488             d,
4489             o;
4490
4491         for(eid in EC){
4492             o = EC[eid];
4493             if(o.skipGC){
4494                 continue;
4495             }
4496             el = o.el;
4497             d = el.dom;
4498             // -------------------------------------------------------
4499             // Determining what is garbage:
4500             // -------------------------------------------------------
4501             // !d
4502             // dom node is null, definitely garbage
4503             // -------------------------------------------------------
4504             // !d.parentNode
4505             // no parentNode == direct orphan, definitely garbage
4506             // -------------------------------------------------------
4507             // !d.offsetParent && !document.getElementById(eid)
4508             // display none elements have no offsetParent so we will
4509             // also try to look it up by it's id. However, check
4510             // offsetParent first so we don't do unneeded lookups.
4511             // This enables collection of elements that are not orphans
4512             // directly, but somewhere up the line they have an orphan
4513             // parent.
4514             // -------------------------------------------------------
4515             if(!d || !d.parentNode || (!d.offsetParent && !DOC.getElementById(eid))){
4516                 if(Ext.enableListenerCollection){
4517                     Ext.EventManager.removeAll(d);
4518                 }
4519                 delete EC[eid];
4520             }
4521         }
4522         // Cleanup IE Object leaks
4523         if (Ext.isIE) {
4524             var t = {};
4525             for (eid in EC) {
4526                 t[eid] = EC[eid];
4527             }
4528             EC = Ext.elCache = t;
4529         }
4530     }
4531 }
4532 El.collectorThreadId = setInterval(garbageCollect, 30000);
4533
4534 var flyFn = function(){};
4535 flyFn.prototype = El.prototype;
4536
4537 // dom is optional
4538 El.Flyweight = function(dom){
4539     this.dom = dom;
4540 };
4541
4542 El.Flyweight.prototype = new flyFn();
4543 El.Flyweight.prototype.isFlyweight = true;
4544 El._flyweights = {};
4545
4546 /**
4547  * <p>Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
4548  * the dom node can be overwritten by other code. Shorthand of {@link Ext.Element#fly}</p>
4549  * <p>Use this to make one-time references to DOM elements which are not going to be accessed again either by
4550  * application code, or by Ext's classes. If accessing an element which will be processed regularly, then {@link Ext#get}
4551  * will be more appropriate to take advantage of the caching provided by the Ext.Element class.</p>
4552  * @param {String/HTMLElement} el The dom node or id
4553  * @param {String} named (optional) Allows for creation of named reusable flyweights to prevent conflicts
4554  * (e.g. internally Ext uses "_global")
4555  * @return {Element} The shared Element object (or null if no matching element was found)
4556  * @member Ext.Element
4557  * @method fly
4558  */
4559 El.fly = function(el, named){
4560     var ret = null;
4561     named = named || '_global';
4562
4563     if (el = Ext.getDom(el)) {
4564         (El._flyweights[named] = El._flyweights[named] || new El.Flyweight()).dom = el;
4565         ret = El._flyweights[named];
4566     }
4567     return ret;
4568 };
4569
4570 /**
4571  * Retrieves Ext.Element objects.
4572  * <p><b>This method does not retrieve {@link Ext.Component Component}s.</b> This method
4573  * retrieves Ext.Element objects which encapsulate DOM elements. To retrieve a Component by
4574  * its ID, use {@link Ext.ComponentMgr#get}.</p>
4575  * <p>Uses simple caching to consistently return the same object. Automatically fixes if an
4576  * object was recreated with the same id via AJAX or DOM.</p>
4577  * Shorthand of {@link Ext.Element#get}
4578  * @param {Mixed} el The id of the node, a DOM Node or an existing Element.
4579  * @return {Element} The Element object (or null if no matching element was found)
4580  * @member Ext
4581  * @method get
4582  */
4583 Ext.get = El.get;
4584
4585 /**
4586  * <p>Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
4587  * the dom node can be overwritten by other code. Shorthand of {@link Ext.Element#fly}</p>
4588  * <p>Use this to make one-time references to DOM elements which are not going to be accessed again either by
4589  * application code, or by Ext's classes. If accessing an element which will be processed regularly, then {@link Ext#get}
4590  * will be more appropriate to take advantage of the caching provided by the Ext.Element class.</p>
4591  * @param {String/HTMLElement} el The dom node or id
4592  * @param {String} named (optional) Allows for creation of named reusable flyweights to prevent conflicts
4593  * (e.g. internally Ext uses "_global")
4594  * @return {Element} The shared Element object (or null if no matching element was found)
4595  * @member Ext
4596  * @method fly
4597  */
4598 Ext.fly = El.fly;
4599
4600 // speedy lookup for elements never to box adjust
4601 var noBoxAdjust = Ext.isStrict ? {
4602     select:1
4603 } : {
4604     input:1, select:1, textarea:1
4605 };
4606 if(Ext.isIE || Ext.isGecko){
4607     noBoxAdjust['button'] = 1;
4608 }
4609
4610
4611 Ext.EventManager.on(window, 'unload', function(){
4612     delete EC;
4613     delete El._flyweights;
4614 });
4615 })();
4616 /**
4617  * @class Ext.Element
4618  */
4619 Ext.Element.addMethods({
4620     /**
4621      * Stops the specified event(s) from bubbling and optionally prevents the default action
4622      * @param {String/Array} eventName an event / array of events to stop from bubbling
4623      * @param {Boolean} preventDefault (optional) true to prevent the default action too
4624      * @return {Ext.Element} this
4625      */
4626     swallowEvent : function(eventName, preventDefault){
4627         var me = this;
4628         function fn(e){
4629             e.stopPropagation();
4630             if(preventDefault){
4631                 e.preventDefault();
4632             }
4633         }
4634         if(Ext.isArray(eventName)){
4635             Ext.each(eventName, function(e) {
4636                  me.on(e, fn);
4637             });
4638             return me;
4639         }
4640         me.on(eventName, fn);
4641         return me;
4642     },
4643
4644     /**
4645      * Create an event handler on this element such that when the event fires and is handled by this element,
4646      * it will be relayed to another object (i.e., fired again as if it originated from that object instead).
4647      * @param {String} eventName The type of event to relay
4648      * @param {Object} object Any object that extends {@link Ext.util.Observable} that will provide the context
4649      * for firing the relayed event
4650      */
4651     relayEvent : function(eventName, observable){
4652         this.on(eventName, function(e){
4653             observable.fireEvent(eventName, e);
4654         });
4655     },
4656
4657     /**
4658      * Removes worthless text nodes
4659      * @param {Boolean} forceReclean (optional) By default the element
4660      * keeps track if it has been cleaned already so
4661      * you can call this over and over. However, if you update the element and
4662      * need to force a reclean, you can pass true.
4663      */
4664     clean : function(forceReclean){
4665         var me = this,
4666             dom = me.dom,
4667             n = dom.firstChild,
4668             ni = -1;
4669
4670         if(Ext.Element.data(dom, 'isCleaned') && forceReclean !== true){
4671             return me;
4672         }
4673
4674         while(n){
4675             var nx = n.nextSibling;
4676             if(n.nodeType == 3 && !/\S/.test(n.nodeValue)){
4677                 dom.removeChild(n);
4678             }else{
4679                 n.nodeIndex = ++ni;
4680             }
4681             n = nx;
4682         }
4683         Ext.Element.data(dom, 'isCleaned', true);
4684         return me;
4685     },
4686
4687     /**
4688      * Direct access to the Updater {@link Ext.Updater#update} method. The method takes the same object
4689      * parameter as {@link Ext.Updater#update}
4690      * @return {Ext.Element} this
4691      */
4692     load : function(){
4693         var um = this.getUpdater();
4694         um.update.apply(um, arguments);
4695         return this;
4696     },
4697
4698     /**
4699     * Gets this element's {@link Ext.Updater Updater}
4700     * @return {Ext.Updater} The Updater
4701     */
4702     getUpdater : function(){
4703         return this.updateManager || (this.updateManager = new Ext.Updater(this));
4704     },
4705
4706     /**
4707     * Update the innerHTML of this element, optionally searching for and processing scripts
4708     * @param {String} html The new HTML
4709     * @param {Boolean} loadScripts (optional) True to look for and process scripts (defaults to false)
4710     * @param {Function} callback (optional) For async script loading you can be notified when the update completes
4711     * @return {Ext.Element} this
4712      */
4713     update : function(html, loadScripts, callback){
4714         if (!this.dom) {
4715             return this;
4716         }
4717         html = html || "";
4718
4719         if(loadScripts !== true){
4720             this.dom.innerHTML = html;
4721             if(Ext.isFunction(callback)){
4722                 callback();
4723             }
4724             return this;
4725         }
4726
4727         var id = Ext.id(),
4728             dom = this.dom;
4729
4730         html += '<span id="' + id + '"></span>';
4731
4732         Ext.lib.Event.onAvailable(id, function(){
4733             var DOC = document,
4734                 hd = DOC.getElementsByTagName("head")[0],
4735                 re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig,
4736                 srcRe = /\ssrc=([\'\"])(.*?)\1/i,
4737                 typeRe = /\stype=([\'\"])(.*?)\1/i,
4738                 match,
4739                 attrs,
4740                 srcMatch,
4741                 typeMatch,
4742                 el,
4743                 s;
4744
4745             while((match = re.exec(html))){
4746                 attrs = match[1];
4747                 srcMatch = attrs ? attrs.match(srcRe) : false;
4748                 if(srcMatch && srcMatch[2]){
4749                    s = DOC.createElement("script");
4750                    s.src = srcMatch[2];
4751                    typeMatch = attrs.match(typeRe);
4752                    if(typeMatch && typeMatch[2]){
4753                        s.type = typeMatch[2];
4754                    }
4755                    hd.appendChild(s);
4756                 }else if(match[2] && match[2].length > 0){
4757                     if(window.execScript) {
4758                        window.execScript(match[2]);
4759                     } else {
4760                        window.eval(match[2]);
4761                     }
4762                 }
4763             }
4764             el = DOC.getElementById(id);
4765             if(el){Ext.removeNode(el);}
4766             if(Ext.isFunction(callback)){
4767                 callback();
4768             }
4769         });
4770         dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
4771         return this;
4772     },
4773
4774     // inherit docs, overridden so we can add removeAnchor
4775     removeAllListeners : function(){
4776         this.removeAnchor();
4777         Ext.EventManager.removeAll(this.dom);
4778         return this;
4779     },
4780
4781     /**
4782      * Creates a proxy element of this element
4783      * @param {String/Object} config The class name of the proxy element or a DomHelper config object
4784      * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
4785      * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
4786      * @return {Ext.Element} The new proxy element
4787      */
4788     createProxy : function(config, renderTo, matchBox){
4789         config = Ext.isObject(config) ? config : {tag : "div", cls: config};
4790
4791         var me = this,
4792             proxy = renderTo ? Ext.DomHelper.append(renderTo, config, true) :
4793                                Ext.DomHelper.insertBefore(me.dom, config, true);
4794
4795         if(matchBox && me.setBox && me.getBox){ // check to make sure Element.position.js is loaded
4796            proxy.setBox(me.getBox());
4797         }
4798         return proxy;
4799     }
4800 });
4801
4802 Ext.Element.prototype.getUpdateManager = Ext.Element.prototype.getUpdater;
4803 /**
4804  * @class Ext.Element
4805  */
4806 Ext.Element.addMethods({
4807     /**
4808      * Gets the x,y coordinates specified by the anchor position on the element.
4809      * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo}
4810      * for details on supported anchor positions.
4811      * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead
4812      * of page coordinates
4813      * @param {Object} size (optional) An object containing the size to use for calculating anchor position
4814      * {width: (target width), height: (target height)} (defaults to the element's current size)
4815      * @return {Array} [x, y] An array containing the element's x and y coordinates
4816      */
4817     getAnchorXY : function(anchor, local, s){
4818         //Passing a different size is useful for pre-calculating anchors,
4819         //especially for anchored animations that change the el size.
4820                 anchor = (anchor || "tl").toLowerCase();
4821         s = s || {};
4822         
4823         var me = this,        
4824                 vp = me.dom == document.body || me.dom == document,
4825                 w = s.width || vp ? Ext.lib.Dom.getViewWidth() : me.getWidth(),
4826                 h = s.height || vp ? Ext.lib.Dom.getViewHeight() : me.getHeight(),                              
4827                 xy,             
4828                 r = Math.round,
4829                 o = me.getXY(),
4830                 scroll = me.getScroll(),
4831                 extraX = vp ? scroll.left : !local ? o[0] : 0,
4832                 extraY = vp ? scroll.top : !local ? o[1] : 0,
4833                 hash = {
4834                         c  : [r(w * 0.5), r(h * 0.5)],
4835                         t  : [r(w * 0.5), 0],
4836                         l  : [0, r(h * 0.5)],
4837                         r  : [w, r(h * 0.5)],
4838                         b  : [r(w * 0.5), h],
4839                         tl : [0, 0],    
4840                         bl : [0, h],
4841                         br : [w, h],
4842                         tr : [w, 0]
4843                 };
4844         
4845         xy = hash[anchor];      
4846         return [xy[0] + extraX, xy[1] + extraY]; 
4847     },
4848
4849     /**
4850      * Anchors an element to another element and realigns it when the window is resized.
4851      * @param {Mixed} element The element to align to.
4852      * @param {String} position The position to align to.
4853      * @param {Array} offsets (optional) Offset the positioning by [x, y]
4854      * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
4855      * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
4856      * is a number, it is used as the buffer delay (defaults to 50ms).
4857      * @param {Function} callback The function to call after the animation finishes
4858      * @return {Ext.Element} this
4859      */
4860     anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){        
4861             var me = this,
4862             dom = me.dom,
4863             scroll = !Ext.isEmpty(monitorScroll),
4864             action = function(){
4865                 Ext.fly(dom).alignTo(el, alignment, offsets, animate);
4866                 Ext.callback(callback, Ext.fly(dom));
4867             },
4868             anchor = this.getAnchor();
4869             
4870         // previous listener anchor, remove it
4871         this.removeAnchor();
4872         Ext.apply(anchor, {
4873             fn: action,
4874             scroll: scroll
4875         });
4876
4877         Ext.EventManager.onWindowResize(action, null);
4878         
4879         if(scroll){
4880             Ext.EventManager.on(window, 'scroll', action, null,
4881                 {buffer: !isNaN(monitorScroll) ? monitorScroll : 50});
4882         }
4883         action.call(me); // align immediately
4884         return me;
4885     },
4886     
4887     /**
4888      * Remove any anchor to this element. See {@link #anchorTo}.
4889      * @return {Ext.Element} this
4890      */
4891     removeAnchor : function(){
4892         var me = this,
4893             anchor = this.getAnchor();
4894             
4895         if(anchor && anchor.fn){
4896             Ext.EventManager.removeResizeListener(anchor.fn);
4897             if(anchor.scroll){
4898                 Ext.EventManager.un(window, 'scroll', anchor.fn);
4899             }
4900             delete anchor.fn;
4901         }
4902         return me;
4903     },
4904     
4905     // private
4906     getAnchor : function(){
4907         var data = Ext.Element.data,
4908             dom = this.dom;
4909             if (!dom) {
4910                 return;
4911             }
4912             var anchor = data(dom, '_anchor');
4913             
4914         if(!anchor){
4915             anchor = data(dom, '_anchor', {});
4916         }
4917         return anchor;
4918     },
4919
4920     /**
4921      * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
4922      * supported position values.
4923      * @param {Mixed} element The element to align to.
4924      * @param {String} position (optional, defaults to "tl-bl?") The position to align to.
4925      * @param {Array} offsets (optional) Offset the positioning by [x, y]
4926      * @return {Array} [x, y]
4927      */
4928     getAlignToXY : function(el, p, o){      
4929         el = Ext.get(el);
4930         
4931         if(!el || !el.dom){
4932             throw "Element.alignToXY with an element that doesn't exist";
4933         }
4934         
4935         o = o || [0,0];
4936         p = (!p || p == "?" ? "tl-bl?" : (!/-/.test(p) && p !== "" ? "tl-" + p : p || "tl-bl")).toLowerCase();       
4937                 
4938         var me = this,
4939                 d = me.dom,
4940                 a1,
4941                 a2,
4942                 x,
4943                 y,
4944                 //constrain the aligned el to viewport if necessary
4945                 w,
4946                 h,
4947                 r,
4948                 dw = Ext.lib.Dom.getViewWidth() -10, // 10px of margin for ie
4949                 dh = Ext.lib.Dom.getViewHeight()-10, // 10px of margin for ie
4950                 p1y,
4951                 p1x,            
4952                 p2y,
4953                 p2x,
4954                 swapY,
4955                 swapX,
4956                 doc = document,
4957                 docElement = doc.documentElement,
4958                 docBody = doc.body,
4959                 scrollX = (docElement.scrollLeft || docBody.scrollLeft || 0)+5,
4960                 scrollY = (docElement.scrollTop || docBody.scrollTop || 0)+5,
4961                 c = false, //constrain to viewport
4962                 p1 = "", 
4963                 p2 = "",
4964                 m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
4965         
4966         if(!m){
4967            throw "Element.alignTo with an invalid alignment " + p;
4968         }
4969         
4970         p1 = m[1]; 
4971         p2 = m[2]; 
4972         c = !!m[3];
4973
4974         //Subtract the aligned el's internal xy from the target's offset xy
4975         //plus custom offset to get the aligned el's new offset xy
4976         a1 = me.getAnchorXY(p1, true);
4977         a2 = el.getAnchorXY(p2, false);
4978
4979         x = a2[0] - a1[0] + o[0];
4980         y = a2[1] - a1[1] + o[1];
4981
4982         if(c){    
4983                w = me.getWidth();
4984            h = me.getHeight();
4985            r = el.getRegion();       
4986            //If we are at a viewport boundary and the aligned el is anchored on a target border that is
4987            //perpendicular to the vp border, allow the aligned el to slide on that border,
4988            //otherwise swap the aligned el to the opposite border of the target.
4989            p1y = p1.charAt(0);
4990            p1x = p1.charAt(p1.length-1);
4991            p2y = p2.charAt(0);
4992            p2x = p2.charAt(p2.length-1);
4993            swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
4994            swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));          
4995            
4996
4997            if (x + w > dw + scrollX) {
4998                 x = swapX ? r.left-w : dw+scrollX-w;
4999            }
5000            if (x < scrollX) {
5001                x = swapX ? r.right : scrollX;
5002            }
5003            if (y + h > dh + scrollY) {
5004                 y = swapY ? r.top-h : dh+scrollY-h;
5005             }
5006            if (y < scrollY){
5007                y = swapY ? r.bottom : scrollY;
5008            }
5009         }
5010         return [x,y];
5011     },
5012
5013     /**
5014      * Aligns this element with another element relative to the specified anchor points. If the other element is the
5015      * document it aligns it to the viewport.
5016      * The position parameter is optional, and can be specified in any one of the following formats:
5017      * <ul>
5018      *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
5019      *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
5020      *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
5021      *       deprecated in favor of the newer two anchor syntax below</i>.</li>
5022      *   <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
5023      *       element's anchor point, and the second value is used as the target's anchor point.</li>
5024      * </ul>
5025      * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
5026      * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
5027      * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
5028      * that specified in order to enforce the viewport constraints.
5029      * Following are all of the supported anchor positions:
5030 <pre>
5031 Value  Description
5032 -----  -----------------------------
5033 tl     The top left corner (default)
5034 t      The center of the top edge
5035 tr     The top right corner
5036 l      The center of the left edge
5037 c      In the center of the element
5038 r      The center of the right edge
5039 bl     The bottom left corner
5040 b      The center of the bottom edge
5041 br     The bottom right corner
5042 </pre>
5043 Example Usage:
5044 <pre><code>
5045 // align el to other-el using the default positioning ("tl-bl", non-constrained)
5046 el.alignTo("other-el");
5047
5048 // align the top left corner of el with the top right corner of other-el (constrained to viewport)
5049 el.alignTo("other-el", "tr?");
5050
5051 // align the bottom right corner of el with the center left edge of other-el
5052 el.alignTo("other-el", "br-l?");
5053
5054 // align the center of el with the bottom left corner of other-el and
5055 // adjust the x position by -6 pixels (and the y position by 0)
5056 el.alignTo("other-el", "c-bl", [-6, 0]);
5057 </code></pre>
5058      * @param {Mixed} element The element to align to.
5059      * @param {String} position (optional, defaults to "tl-bl?") The position to align to.
5060      * @param {Array} offsets (optional) Offset the positioning by [x, y]
5061      * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
5062      * @return {Ext.Element} this
5063      */
5064     alignTo : function(element, position, offsets, animate){
5065             var me = this;
5066         return me.setXY(me.getAlignToXY(element, position, offsets),
5067                                 me.preanim && !!animate ? me.preanim(arguments, 3) : false);
5068     },
5069     
5070     // private ==>  used outside of core
5071     adjustForConstraints : function(xy, parent, offsets){
5072         return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
5073     },
5074
5075     // private ==>  used outside of core
5076     getConstrainToXY : function(el, local, offsets, proposedXY){   
5077             var os = {top:0, left:0, bottom:0, right: 0};
5078
5079         return function(el, local, offsets, proposedXY){
5080             el = Ext.get(el);
5081             offsets = offsets ? Ext.applyIf(offsets, os) : os;
5082
5083             var vw, vh, vx = 0, vy = 0;
5084             if(el.dom == document.body || el.dom == document){
5085                 vw =Ext.lib.Dom.getViewWidth();
5086                 vh = Ext.lib.Dom.getViewHeight();
5087             }else{
5088                 vw = el.dom.clientWidth;
5089                 vh = el.dom.clientHeight;
5090                 if(!local){
5091                     var vxy = el.getXY();
5092                     vx = vxy[0];
5093                     vy = vxy[1];
5094                 }
5095             }
5096
5097             var s = el.getScroll();
5098
5099             vx += offsets.left + s.left;
5100             vy += offsets.top + s.top;
5101
5102             vw -= offsets.right;
5103             vh -= offsets.bottom;
5104
5105             var vr = vx+vw;
5106             var vb = vy+vh;
5107
5108             var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
5109             var x = xy[0], y = xy[1];
5110             var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
5111
5112             // only move it if it needs it
5113             var moved = false;
5114
5115             // first validate right/bottom
5116             if((x + w) > vr){
5117                 x = vr - w;
5118                 moved = true;
5119             }
5120             if((y + h) > vb){
5121                 y = vb - h;
5122                 moved = true;
5123             }
5124             // then make sure top/left isn't negative
5125             if(x < vx){
5126                 x = vx;
5127                 moved = true;
5128             }
5129             if(y < vy){
5130                 y = vy;
5131                 moved = true;
5132             }
5133             return moved ? [x, y] : false;
5134         };
5135     }(),
5136             
5137             
5138                 
5139 //         el = Ext.get(el);
5140 //         offsets = Ext.applyIf(offsets || {}, {top : 0, left : 0, bottom : 0, right : 0});
5141
5142 //         var  me = this,
5143 //              doc = document,
5144 //              s = el.getScroll(),
5145 //              vxy = el.getXY(),
5146 //              vx = offsets.left + s.left, 
5147 //              vy = offsets.top + s.top,               
5148 //              vw = -offsets.right, 
5149 //              vh = -offsets.bottom, 
5150 //              vr,
5151 //              vb,
5152 //              xy = proposedXY || (!local ? me.getXY() : [me.getLeft(true), me.getTop(true)]),
5153 //              x = xy[0],
5154 //              y = xy[1],
5155 //              w = me.dom.offsetWidth, h = me.dom.offsetHeight,
5156 //              moved = false; // only move it if it needs it
5157 //       
5158 //              
5159 //         if(el.dom == doc.body || el.dom == doc){
5160 //             vw += Ext.lib.Dom.getViewWidth();
5161 //             vh += Ext.lib.Dom.getViewHeight();
5162 //         }else{
5163 //             vw += el.dom.clientWidth;
5164 //             vh += el.dom.clientHeight;
5165 //             if(!local){                    
5166 //                 vx += vxy[0];
5167 //                 vy += vxy[1];
5168 //             }
5169 //         }
5170
5171 //         // first validate right/bottom
5172 //         if(x + w > vx + vw){
5173 //             x = vx + vw - w;
5174 //             moved = true;
5175 //         }
5176 //         if(y + h > vy + vh){
5177 //             y = vy + vh - h;
5178 //             moved = true;
5179 //         }
5180 //         // then make sure top/left isn't negative
5181 //         if(x < vx){
5182 //             x = vx;
5183 //             moved = true;
5184 //         }
5185 //         if(y < vy){
5186 //             y = vy;
5187 //             moved = true;
5188 //         }
5189 //         return moved ? [x, y] : false;
5190 //    },
5191     
5192     /**
5193     * Calculates the x, y to center this element on the screen
5194     * @return {Array} The x, y values [x, y]
5195     */
5196     getCenterXY : function(){
5197         return this.getAlignToXY(document, 'c-c');
5198     },
5199
5200     /**
5201     * Centers the Element in either the viewport, or another Element.
5202     * @param {Mixed} centerIn (optional) The element in which to center the element.
5203     */
5204     center : function(centerIn){
5205         return this.alignTo(centerIn || document, 'c-c');        
5206     }    
5207 });
5208 /**\r
5209  * @class Ext.Element\r
5210  */\r
5211 Ext.Element.addMethods(function(){\r
5212         var PARENTNODE = 'parentNode',\r
5213                 NEXTSIBLING = 'nextSibling',\r
5214                 PREVIOUSSIBLING = 'previousSibling',\r
5215                 DQ = Ext.DomQuery,\r
5216                 GET = Ext.get;\r
5217         \r
5218         return {\r
5219                 /**\r
5220              * 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
5221              * @param {String} selector The simple selector to test\r
5222              * @param {Number/Mixed} maxDepth (optional) The max depth to search as a number or element (defaults to 50 || document.body)\r
5223              * @param {Boolean} returnEl (optional) True to return a Ext.Element object instead of DOM node\r
5224              * @return {HTMLElement} The matching DOM node (or null if no match was found)\r
5225              */\r
5226             findParent : function(simpleSelector, maxDepth, returnEl){\r
5227                 var p = this.dom,\r
5228                         b = document.body, \r
5229                         depth = 0,                      \r
5230                         stopEl;         \r
5231             if(Ext.isGecko && Object.prototype.toString.call(p) == '[object XULElement]') {\r
5232                 return null;\r
5233             }\r
5234                 maxDepth = maxDepth || 50;\r
5235                 if (isNaN(maxDepth)) {\r
5236                     stopEl = Ext.getDom(maxDepth);\r
5237                     maxDepth = Number.MAX_VALUE;\r
5238                 }\r
5239                 while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){\r
5240                     if(DQ.is(p, simpleSelector)){\r
5241                         return returnEl ? GET(p) : p;\r
5242                     }\r
5243                     depth++;\r
5244                     p = p.parentNode;\r
5245                 }\r
5246                 return null;\r
5247             },\r
5248         \r
5249             /**\r
5250              * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)\r
5251              * @param {String} selector The simple selector to test\r
5252              * @param {Number/Mixed} maxDepth (optional) The max depth to\r
5253                     search as a number or element (defaults to 10 || document.body)\r
5254              * @param {Boolean} returnEl (optional) True to return a Ext.Element object instead of DOM node\r
5255              * @return {HTMLElement} The matching DOM node (or null if no match was found)\r
5256              */\r
5257             findParentNode : function(simpleSelector, maxDepth, returnEl){\r
5258                 var p = Ext.fly(this.dom.parentNode, '_internal');\r
5259                 return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;\r
5260             },\r
5261         \r
5262             /**\r
5263              * 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
5264              * This is a shortcut for findParentNode() that always returns an Ext.Element.\r
5265              * @param {String} selector The simple selector to test\r
5266              * @param {Number/Mixed} maxDepth (optional) The max depth to\r
5267                     search as a number or element (defaults to 10 || document.body)\r
5268              * @return {Ext.Element} The matching DOM node (or null if no match was found)\r
5269              */\r
5270             up : function(simpleSelector, maxDepth){\r
5271                 return this.findParentNode(simpleSelector, maxDepth, true);\r
5272             },\r
5273         \r
5274             /**\r
5275              * Creates a {@link Ext.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).\r
5276              * @param {String} selector The CSS selector\r
5277              * @return {CompositeElement/CompositeElementLite} The composite element\r
5278              */\r
5279             select : function(selector){\r
5280                 return Ext.Element.select(selector, this.dom);\r
5281             },\r
5282         \r
5283             /**\r
5284              * Selects child nodes based on the passed CSS selector (the selector should not contain an id).\r
5285              * @param {String} selector The CSS selector\r
5286              * @return {Array} An array of the matched nodes\r
5287              */\r
5288             query : function(selector){\r
5289                 return DQ.select(selector, this.dom);\r
5290             },\r
5291         \r
5292             /**\r
5293              * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).\r
5294              * @param {String} selector The CSS selector\r
5295              * @param {Boolean} returnDom (optional) True to return the DOM node instead of Ext.Element (defaults to false)\r
5296              * @return {HTMLElement/Ext.Element} The child Ext.Element (or DOM node if returnDom = true)\r
5297              */\r
5298             child : function(selector, returnDom){\r
5299                 var n = DQ.selectNode(selector, this.dom);\r
5300                 return returnDom ? n : GET(n);\r
5301             },\r
5302         \r
5303             /**\r
5304              * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).\r
5305              * @param {String} selector The CSS selector\r
5306              * @param {Boolean} returnDom (optional) True to return the DOM node instead of Ext.Element (defaults to false)\r
5307              * @return {HTMLElement/Ext.Element} The child Ext.Element (or DOM node if returnDom = true)\r
5308              */\r
5309             down : function(selector, returnDom){\r
5310                 var n = DQ.selectNode(" > " + selector, this.dom);\r
5311                 return returnDom ? n : GET(n);\r
5312             },\r
5313         \r
5314                  /**\r
5315              * Gets the parent node for this element, optionally chaining up trying to match a selector\r
5316              * @param {String} selector (optional) Find a parent node that matches the passed simple selector\r
5317              * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.Element\r
5318              * @return {Ext.Element/HTMLElement} The parent node or null\r
5319                  */\r
5320             parent : function(selector, returnDom){\r
5321                 return this.matchNode(PARENTNODE, PARENTNODE, selector, returnDom);\r
5322             },\r
5323         \r
5324              /**\r
5325              * Gets the next sibling, skipping text nodes\r
5326              * @param {String} selector (optional) Find the next sibling that matches the passed simple selector\r
5327              * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.Element\r
5328              * @return {Ext.Element/HTMLElement} The next sibling or null\r
5329                  */\r
5330             next : function(selector, returnDom){\r
5331                 return this.matchNode(NEXTSIBLING, NEXTSIBLING, selector, returnDom);\r
5332             },\r
5333         \r
5334             /**\r
5335              * Gets the previous sibling, skipping text nodes\r
5336              * @param {String} selector (optional) Find the previous sibling that matches the passed simple selector\r
5337              * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.Element\r
5338              * @return {Ext.Element/HTMLElement} The previous sibling or null\r
5339                  */\r
5340             prev : function(selector, returnDom){\r
5341                 return this.matchNode(PREVIOUSSIBLING, PREVIOUSSIBLING, selector, returnDom);\r
5342             },\r
5343         \r
5344         \r
5345             /**\r
5346              * Gets the first child, skipping text nodes\r
5347              * @param {String} selector (optional) Find the next sibling that matches the passed simple selector\r
5348              * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.Element\r
5349              * @return {Ext.Element/HTMLElement} The first child or null\r
5350                  */\r
5351             first : function(selector, returnDom){\r
5352                 return this.matchNode(NEXTSIBLING, 'firstChild', selector, returnDom);\r
5353             },\r
5354         \r
5355             /**\r
5356              * Gets the last child, skipping text nodes\r
5357              * @param {String} selector (optional) Find the previous sibling that matches the passed simple selector\r
5358              * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.Element\r
5359              * @return {Ext.Element/HTMLElement} The last child or null\r
5360                  */\r
5361             last : function(selector, returnDom){\r
5362                 return this.matchNode(PREVIOUSSIBLING, 'lastChild', selector, returnDom);\r
5363             },\r
5364             \r
5365             matchNode : function(dir, start, selector, returnDom){\r
5366                 var n = this.dom[start];\r
5367                 while(n){\r
5368                     if(n.nodeType == 1 && (!selector || DQ.is(n, selector))){\r
5369                         return !returnDom ? GET(n) : n;\r
5370                     }\r
5371                     n = n[dir];\r
5372                 }\r
5373                 return null;\r
5374             }   \r
5375     }\r
5376 }());/**\r
5377  * @class Ext.Element\r
5378  */\r
5379 Ext.Element.addMethods({\r
5380     /**\r
5381      * Creates a {@link Ext.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).\r
5382      * @param {String} selector The CSS selector\r
5383      * @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
5384      * @return {CompositeElement/CompositeElementLite} The composite element\r
5385      */\r
5386     select : function(selector, unique){\r
5387         return Ext.Element.select(selector, unique, this.dom);\r
5388     }\r
5389 });/**\r
5390  * @class Ext.Element\r
5391  */\r
5392 Ext.Element.addMethods(\r
5393 function() {\r
5394         var GETDOM = Ext.getDom,\r
5395                 GET = Ext.get,\r
5396                 DH = Ext.DomHelper;\r
5397         \r
5398         return {\r
5399             /**\r
5400              * Appends the passed element(s) to this element\r
5401              * @param {String/HTMLElement/Array/Element/CompositeElement} el\r
5402              * @return {Ext.Element} this\r
5403              */\r
5404             appendChild: function(el){        \r
5405                 return GET(el).appendTo(this);        \r
5406             },\r
5407         \r
5408             /**\r
5409              * Appends this element to the passed element\r
5410              * @param {Mixed} el The new parent element\r
5411              * @return {Ext.Element} this\r
5412              */\r
5413             appendTo: function(el){        \r
5414                 GETDOM(el).appendChild(this.dom);        \r
5415                 return this;\r
5416             },\r
5417         \r
5418             /**\r
5419              * Inserts this element before the passed element in the DOM\r
5420              * @param {Mixed} el The element before which this element will be inserted\r
5421              * @return {Ext.Element} this\r
5422              */\r
5423             insertBefore: function(el){                   \r
5424                 (el = GETDOM(el)).parentNode.insertBefore(this.dom, el);\r
5425                 return this;\r
5426             },\r
5427         \r
5428             /**\r
5429              * Inserts this element after the passed element in the DOM\r
5430              * @param {Mixed} el The element to insert after\r
5431              * @return {Ext.Element} this\r
5432              */\r
5433             insertAfter: function(el){\r
5434                 (el = GETDOM(el)).parentNode.insertBefore(this.dom, el.nextSibling);\r
5435                 return this;\r
5436             },\r
5437         \r
5438             /**\r
5439              * Inserts (or creates) an element (or DomHelper config) as the first child of this element\r
5440              * @param {Mixed/Object} el The id or element to insert or a DomHelper config to create and insert\r
5441              * @return {Ext.Element} The new child\r
5442              */\r
5443             insertFirst: function(el, returnDom){\r
5444             el = el || {};\r
5445             if(el.nodeType || el.dom || typeof el == 'string'){ // element\r
5446                 el = GETDOM(el);\r
5447                 this.dom.insertBefore(el, this.dom.firstChild);\r
5448                 return !returnDom ? GET(el) : el;\r
5449             }else{ // dh config\r
5450                 return this.createChild(el, this.dom.firstChild, returnDom);\r
5451             }\r
5452         },\r
5453         \r
5454             /**\r
5455              * Replaces the passed element with this element\r
5456              * @param {Mixed} el The element to replace\r
5457              * @return {Ext.Element} this\r
5458              */\r
5459             replace: function(el){\r
5460                 el = GET(el);\r
5461                 this.insertBefore(el);\r
5462                 el.remove();\r
5463                 return this;\r
5464             },\r
5465         \r
5466             /**\r
5467              * Replaces this element with the passed element\r
5468              * @param {Mixed/Object} el The new element or a DomHelper config of an element to create\r
5469              * @return {Ext.Element} this\r
5470              */\r
5471             replaceWith: function(el){\r
5472                     var me = this;\r
5473                 \r
5474             if(el.nodeType || el.dom || typeof el == 'string'){\r
5475                 el = GETDOM(el);\r
5476                 me.dom.parentNode.insertBefore(el, me.dom);\r
5477             }else{\r
5478                 el = DH.insertBefore(me.dom, el);\r
5479             }\r
5480                 \r
5481                 delete Ext.elCache[me.id];\r
5482                 Ext.removeNode(me.dom);      \r
5483                 me.id = Ext.id(me.dom = el);\r
5484                 Ext.Element.addToCache(me.isFlyweight ? new Ext.Element(me.dom) : me);     \r
5485             return me;\r
5486             },\r
5487             \r
5488                 /**\r
5489                  * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.\r
5490                  * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be\r
5491                  * automatically generated with the specified attributes.\r
5492                  * @param {HTMLElement} insertBefore (optional) a child element of this element\r
5493                  * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element\r
5494                  * @return {Ext.Element} The new child element\r
5495                  */\r
5496                 createChild: function(config, insertBefore, returnDom){\r
5497                     config = config || {tag:'div'};\r
5498                     return insertBefore ? \r
5499                            DH.insertBefore(insertBefore, config, returnDom !== true) :  \r
5500                            DH[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);\r
5501                 },\r
5502                 \r
5503                 /**\r
5504                  * Creates and wraps this element with another element\r
5505                  * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div\r
5506                  * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Ext.Element\r
5507                  * @return {HTMLElement/Element} The newly created wrapper element\r
5508                  */\r
5509                 wrap: function(config, returnDom){        \r
5510                     var newEl = DH.insertBefore(this.dom, config || {tag: "div"}, !returnDom);\r
5511                     newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);\r
5512                     return newEl;\r
5513                 },\r
5514                 \r
5515                 /**\r
5516                  * Inserts an html fragment into this element\r
5517                  * @param {String} where Where to insert the html in relation to this element - beforeBegin, afterBegin, beforeEnd, afterEnd.\r
5518                  * @param {String} html The HTML fragment\r
5519                  * @param {Boolean} returnEl (optional) True to return an Ext.Element (defaults to false)\r
5520                  * @return {HTMLElement/Ext.Element} The inserted node (or nearest related if more than 1 inserted)\r
5521                  */\r
5522                 insertHtml : function(where, html, returnEl){\r
5523                     var el = DH.insertHtml(where, this.dom, html);\r
5524                     return returnEl ? Ext.get(el) : el;\r
5525                 }\r
5526         }\r
5527 }());/**\r
5528  * @class Ext.Element\r
5529  */\r
5530 Ext.apply(Ext.Element.prototype, function() {\r
5531         var GETDOM = Ext.getDom,\r
5532                 GET = Ext.get,\r
5533                 DH = Ext.DomHelper;\r
5534         \r
5535         return {        \r
5536                 /**\r
5537              * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element\r
5538              * @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
5539              * @param {String} where (optional) 'before' or 'after' defaults to before\r
5540              * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Ext.Element\r
5541              * @return {Ext.Element} The inserted Element. If an array is passed, the last inserted element is returned.\r
5542              */\r
5543             insertSibling: function(el, where, returnDom){\r
5544                 var me = this,\r
5545                         rt,\r
5546                 isAfter = (where || 'before').toLowerCase() == 'after',\r
5547                 insertEl;\r
5548                         \r
5549                 if(Ext.isArray(el)){\r
5550                 insertEl = me;\r
5551                     Ext.each(el, function(e) {\r
5552                             rt = Ext.fly(insertEl, '_internal').insertSibling(e, where, returnDom);\r
5553                     if(isAfter){\r
5554                         insertEl = rt;\r
5555                     }\r
5556                     });\r
5557                     return rt;\r
5558                 }\r
5559                         \r
5560                 el = el || {};\r
5561                 \r
5562             if(el.nodeType || el.dom){\r
5563                 rt = me.dom.parentNode.insertBefore(GETDOM(el), isAfter ? me.dom.nextSibling : me.dom);\r
5564                 if (!returnDom) {\r
5565                     rt = GET(rt);\r
5566                 }\r
5567             }else{\r
5568                 if (isAfter && !me.dom.nextSibling) {\r
5569                     rt = DH.append(me.dom.parentNode, el, !returnDom);\r
5570                 } else {                    \r
5571                     rt = DH[isAfter ? 'insertAfter' : 'insertBefore'](me.dom, el, !returnDom);\r
5572                 }\r
5573             }\r
5574                 return rt;\r
5575             }\r
5576     };\r
5577 }());/**
5578  * @class Ext.Element
5579  */
5580 Ext.Element.addMethods(function(){
5581     // local style camelizing for speed
5582     var propCache = {},
5583         camelRe = /(-[a-z])/gi,
5584         classReCache = {},
5585         view = document.defaultView,
5586         propFloat = Ext.isIE ? 'styleFloat' : 'cssFloat',
5587         opacityRe = /alpha\(opacity=(.*)\)/i,
5588         trimRe = /^\s+|\s+$/g,
5589         EL = Ext.Element,
5590         PADDING = "padding",
5591         MARGIN = "margin",
5592         BORDER = "border",
5593         LEFT = "-left",
5594         RIGHT = "-right",
5595         TOP = "-top",
5596         BOTTOM = "-bottom",
5597         WIDTH = "-width",
5598         MATH = Math,
5599         HIDDEN = 'hidden',
5600         ISCLIPPED = 'isClipped',
5601         OVERFLOW = 'overflow',
5602         OVERFLOWX = 'overflow-x',
5603         OVERFLOWY = 'overflow-y',
5604         ORIGINALCLIP = 'originalClip',
5605         // special markup used throughout Ext when box wrapping elements
5606         borders = {l: BORDER + LEFT + WIDTH, r: BORDER + RIGHT + WIDTH, t: BORDER + TOP + WIDTH, b: BORDER + BOTTOM + WIDTH},
5607         paddings = {l: PADDING + LEFT, r: PADDING + RIGHT, t: PADDING + TOP, b: PADDING + BOTTOM},
5608         margins = {l: MARGIN + LEFT, r: MARGIN + RIGHT, t: MARGIN + TOP, b: MARGIN + BOTTOM},
5609         data = Ext.Element.data;
5610
5611
5612     // private
5613     function camelFn(m, a) {
5614         return a.charAt(1).toUpperCase();
5615     }
5616
5617     function chkCache(prop) {
5618         return propCache[prop] || (propCache[prop] = prop == 'float' ? propFloat : prop.replace(camelRe, camelFn));
5619     }
5620
5621     return {
5622         // private  ==> used by Fx
5623         adjustWidth : function(width) {
5624             var me = this;
5625             var isNum = Ext.isNumber(width);
5626             if(isNum && me.autoBoxAdjust && !me.isBorderBox()){
5627                width -= (me.getBorderWidth("lr") + me.getPadding("lr"));
5628             }
5629             return (isNum && width < 0) ? 0 : width;
5630         },
5631
5632         // private   ==> used by Fx
5633         adjustHeight : function(height) {
5634             var me = this;
5635             var isNum = Ext.isNumber(height);
5636             if(isNum && me.autoBoxAdjust && !me.isBorderBox()){
5637                height -= (me.getBorderWidth("tb") + me.getPadding("tb"));
5638             }
5639             return (isNum && height < 0) ? 0 : height;
5640         },
5641
5642
5643         /**
5644          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
5645          * @param {String/Array} className The CSS class to add, or an array of classes
5646          * @return {Ext.Element} this
5647          */
5648         addClass : function(className){
5649             var me = this, i, len, v;
5650             className = Ext.isArray(className) ? className : [className];
5651             for (i=0, len = className.length; i < len; i++) {
5652                 v = className[i];
5653                 if (v) {
5654                     me.dom.className += (!me.hasClass(v) && v ? " " + v : "");
5655                 };
5656             };
5657             return me;
5658         },
5659
5660         /**
5661          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
5662          * @param {String/Array} className The CSS class to add, or an array of classes
5663          * @return {Ext.Element} this
5664          */
5665         radioClass : function(className){
5666             var cn = this.dom.parentNode.childNodes, v;
5667             className = Ext.isArray(className) ? className : [className];
5668             for (var i=0, len = cn.length; i < len; i++) {
5669                 v = cn[i];
5670                 if(v && v.nodeType == 1) {
5671                     Ext.fly(v, '_internal').removeClass(className);
5672                 }
5673             };
5674             return this.addClass(className);
5675         },
5676
5677         /**
5678          * Removes one or more CSS classes from the element.
5679          * @param {String/Array} className The CSS class to remove, or an array of classes
5680          * @return {Ext.Element} this
5681          */
5682         removeClass : function(className){
5683             var me = this, v;
5684             className = Ext.isArray(className) ? className : [className];
5685             if (me.dom && me.dom.className) {
5686                 for (var i=0, len=className.length; i < len; i++) {
5687                     v = className[i];
5688                     if(v) {
5689                         me.dom.className = me.dom.className.replace(
5690                             classReCache[v] = classReCache[v] || new RegExp('(?:^|\\s+)' + v + '(?:\\s+|$)', "g"), " "
5691                         );
5692                     }
5693                 };
5694             }
5695             return me;
5696         },
5697
5698         /**
5699          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
5700          * @param {String} className The CSS class to toggle
5701          * @return {Ext.Element} this
5702          */
5703         toggleClass : function(className){
5704             return this.hasClass(className) ? this.removeClass(className) : this.addClass(className);
5705         },
5706
5707         /**
5708          * Checks if the specified CSS class exists on this element's DOM node.
5709          * @param {String} className The CSS class to check for
5710          * @return {Boolean} True if the class exists, else false
5711          */
5712         hasClass : function(className){
5713             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
5714         },
5715
5716         /**
5717          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
5718          * @param {String} oldClassName The CSS class to replace
5719          * @param {String} newClassName The replacement CSS class
5720          * @return {Ext.Element} this
5721          */
5722         replaceClass : function(oldClassName, newClassName){
5723             return this.removeClass(oldClassName).addClass(newClassName);
5724         },
5725
5726         isStyle : function(style, val) {
5727             return this.getStyle(style) == val;
5728         },
5729
5730         /**
5731          * Normalizes currentStyle and computedStyle.
5732          * @param {String} property The style property whose value is returned.
5733          * @return {String} The current value of the style property for this element.
5734          */
5735         getStyle : function(){
5736             return view && view.getComputedStyle ?
5737                 function(prop){
5738                     var el = this.dom,
5739                         v,
5740                         cs,
5741                         out,
5742                         display,
5743                         wk = Ext.isWebKit,
5744                         display;
5745                         
5746                     if(el == document){
5747                         return null;
5748                     }
5749                     prop = chkCache(prop);
5750                     // Fix bug caused by this: https://bugs.webkit.org/show_bug.cgi?id=13343
5751                     if(wk && /marginRight/.test(prop)){
5752                         display = this.getStyle('display');
5753                         el.style.display = 'inline-block';
5754                     }
5755                     out = (v = el.style[prop]) ? v :
5756                            (cs = view.getComputedStyle(el, "")) ? cs[prop] : null;
5757
5758                     // Webkit returns rgb values for transparent.
5759                     if(wk){
5760                         if(out == 'rgba(0, 0, 0, 0)'){
5761                             out = 'transparent';
5762                         }else if(display){
5763                             el.style.display = display;
5764                         }
5765                     }
5766                     return out;
5767                 } :
5768                 function(prop){
5769                     var el = this.dom,
5770                         m,
5771                         cs;
5772
5773                     if(el == document) return null;
5774                     if (prop == 'opacity') {
5775                         if (el.style.filter.match) {
5776                             if(m = el.style.filter.match(opacityRe)){
5777                                 var fv = parseFloat(m[1]);
5778                                 if(!isNaN(fv)){
5779                                     return fv ? fv / 100 : 0;
5780                                 }
5781                             }
5782                         }
5783                         return 1;
5784                     }
5785                     prop = chkCache(prop);
5786                     return el.style[prop] || ((cs = el.currentStyle) ? cs[prop] : null);
5787                 };
5788         }(),
5789
5790         /**
5791          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
5792          * are convert to standard 6 digit hex color.
5793          * @param {String} attr The css attribute
5794          * @param {String} defaultValue The default value to use when a valid color isn't found
5795          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
5796          * color anims.
5797          */
5798         getColor : function(attr, defaultValue, prefix){
5799             var v = this.getStyle(attr),
5800                 color = Ext.isDefined(prefix) ? prefix : '#',
5801                 h;
5802
5803             if(!v || /transparent|inherit/.test(v)){
5804                 return defaultValue;
5805             }
5806             if(/^r/.test(v)){
5807                 Ext.each(v.slice(4, v.length -1).split(','), function(s){
5808                     h = parseInt(s, 10);
5809                     color += (h < 16 ? '0' : '') + h.toString(16);
5810                 });
5811             }else{
5812                 v = v.replace('#', '');
5813                 color += v.length == 3 ? v.replace(/^(\w)(\w)(\w)$/, '$1$1$2$2$3$3') : v;
5814             }
5815             return(color.length > 5 ? color.toLowerCase() : defaultValue);
5816         },
5817
5818         /**
5819          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
5820          * @param {String/Object} property The style property to be set, or an object of multiple styles.
5821          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
5822          * @return {Ext.Element} this
5823          */
5824         setStyle : function(prop, value){
5825             var tmp,
5826                 style,
5827                 camel;
5828             if (!Ext.isObject(prop)) {
5829                 tmp = {};
5830                 tmp[prop] = value;
5831                 prop = tmp;
5832             }
5833             for (style in prop) {
5834                 value = prop[style];
5835                 style == 'opacity' ?
5836                     this.setOpacity(value) :
5837                     this.dom.style[chkCache(style)] = value;
5838             }
5839             return this;
5840         },
5841
5842         /**
5843          * Set the opacity of the element
5844          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
5845          * @param {Boolean/Object} animate (optional) a standard Element animation config object or <tt>true</tt> for
5846          * the default animation (<tt>{duration: .35, easing: 'easeIn'}</tt>)
5847          * @return {Ext.Element} this
5848          */
5849          setOpacity : function(opacity, animate){
5850             var me = this,
5851                 s = me.dom.style;
5852
5853             if(!animate || !me.anim){
5854                 if(Ext.isIE){
5855                     var opac = opacity < 1 ? 'alpha(opacity=' + opacity * 100 + ')' : '',
5856                     val = s.filter.replace(opacityRe, '').replace(trimRe, '');
5857
5858                     s.zoom = 1;
5859                     s.filter = val + (val.length > 0 ? ' ' : '') + opac;
5860                 }else{
5861                     s.opacity = opacity;
5862                 }
5863             }else{
5864                 me.anim({opacity: {to: opacity}}, me.preanim(arguments, 1), null, .35, 'easeIn');
5865             }
5866             return me;
5867         },
5868
5869         /**
5870          * Clears any opacity settings from this element. Required in some cases for IE.
5871          * @return {Ext.Element} this
5872          */
5873         clearOpacity : function(){
5874             var style = this.dom.style;
5875             if(Ext.isIE){
5876                 if(!Ext.isEmpty(style.filter)){
5877                     style.filter = style.filter.replace(opacityRe, '').replace(trimRe, '');
5878                 }
5879             }else{
5880                 style.opacity = style['-moz-opacity'] = style['-khtml-opacity'] = '';
5881             }
5882             return this;
5883         },
5884
5885         /**
5886          * Returns the offset height of the element
5887          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
5888          * @return {Number} The element's height
5889          */
5890         getHeight : function(contentHeight){
5891             var me = this,
5892                 dom = me.dom,
5893                 hidden = Ext.isIE && me.isStyle('display', 'none'),
5894                 h = MATH.max(dom.offsetHeight, hidden ? 0 : dom.clientHeight) || 0;
5895
5896             h = !contentHeight ? h : h - me.getBorderWidth("tb") - me.getPadding("tb");
5897             return h < 0 ? 0 : h;
5898         },
5899
5900         /**
5901          * Returns the offset width of the element
5902          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
5903          * @return {Number} The element's width
5904          */
5905         getWidth : function(contentWidth){
5906             var me = this,
5907                 dom = me.dom,
5908                 hidden = Ext.isIE && me.isStyle('display', 'none'),
5909                 w = MATH.max(dom.offsetWidth, hidden ? 0 : dom.clientWidth) || 0;
5910             w = !contentWidth ? w : w - me.getBorderWidth("lr") - me.getPadding("lr");
5911             return w < 0 ? 0 : w;
5912         },
5913
5914         /**
5915          * Set the width of this Element.
5916          * @param {Mixed} width The new width. This may be one of:<div class="mdetail-params"><ul>
5917          * <li>A Number specifying the new width in this Element's {@link #defaultUnit}s (by default, pixels).</li>
5918          * <li>A String used to set the CSS width style. Animation may <b>not</b> be used.
5919          * </ul></div>
5920          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
5921          * @return {Ext.Element} this
5922          */
5923         setWidth : function(width, animate){
5924             var me = this;
5925             width = me.adjustWidth(width);
5926             !animate || !me.anim ?
5927                 me.dom.style.width = me.addUnits(width) :
5928                 me.anim({width : {to : width}}, me.preanim(arguments, 1));
5929             return me;
5930         },
5931
5932         /**
5933          * Set the height of this Element.
5934          * <pre><code>
5935 // change the height to 200px and animate with default configuration
5936 Ext.fly('elementId').setHeight(200, true);
5937
5938 // change the height to 150px and animate with a custom configuration
5939 Ext.fly('elId').setHeight(150, {
5940     duration : .5, // animation will have a duration of .5 seconds
5941     // will change the content to "finished"
5942     callback: function(){ this.{@link #update}("finished"); }
5943 });
5944          * </code></pre>
5945          * @param {Mixed} height The new height. This may be one of:<div class="mdetail-params"><ul>
5946          * <li>A Number specifying the new height in this Element's {@link #defaultUnit}s (by default, pixels.)</li>
5947          * <li>A String used to set the CSS height style. Animation may <b>not</b> be used.</li>
5948          * </ul></div>
5949          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
5950          * @return {Ext.Element} this
5951          */
5952          setHeight : function(height, animate){
5953             var me = this;
5954             height = me.adjustHeight(height);
5955             !animate || !me.anim ?
5956                 me.dom.style.height = me.addUnits(height) :
5957                 me.anim({height : {to : height}}, me.preanim(arguments, 1));
5958             return me;
5959         },
5960
5961         /**
5962          * Gets the width of the border(s) for the specified side(s)
5963          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
5964          * passing <tt>'lr'</tt> would get the border <b><u>l</u></b>eft width + the border <b><u>r</u></b>ight width.
5965          * @return {Number} The width of the sides passed added together
5966          */
5967         getBorderWidth : function(side){
5968             return this.addStyles(side, borders);
5969         },
5970
5971         /**
5972          * Gets the width of the padding(s) for the specified side(s)
5973          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
5974          * passing <tt>'lr'</tt> would get the padding <b><u>l</u></b>eft + the padding <b><u>r</u></b>ight.
5975          * @return {Number} The padding of the sides passed added together
5976          */
5977         getPadding : function(side){
5978             return this.addStyles(side, paddings);
5979         },
5980
5981         /**
5982          *  Store the current overflow setting and clip overflow on the element - use <tt>{@link #unclip}</tt> to remove
5983          * @return {Ext.Element} this
5984          */
5985         clip : function(){
5986             var me = this,
5987                 dom = me.dom;
5988
5989             if(!data(dom, ISCLIPPED)){
5990                 data(dom, ISCLIPPED, true);
5991                 data(dom, ORIGINALCLIP, {
5992                     o: me.getStyle(OVERFLOW),
5993                     x: me.getStyle(OVERFLOWX),
5994                     y: me.getStyle(OVERFLOWY)
5995                 });
5996                 me.setStyle(OVERFLOW, HIDDEN);
5997                 me.setStyle(OVERFLOWX, HIDDEN);
5998                 me.setStyle(OVERFLOWY, HIDDEN);
5999             }
6000             return me;
6001         },
6002
6003         /**
6004          *  Return clipping (overflow) to original clipping before <tt>{@link #clip}</tt> was called
6005          * @return {Ext.Element} this
6006          */
6007         unclip : function(){
6008             var me = this,
6009                 dom = me.dom;
6010
6011             if(data(dom, ISCLIPPED)){
6012                 data(dom, ISCLIPPED, false);
6013                 var o = data(dom, ORIGINALCLIP);
6014                 if(o.o){
6015                     me.setStyle(OVERFLOW, o.o);
6016                 }
6017                 if(o.x){
6018                     me.setStyle(OVERFLOWX, o.x);
6019                 }
6020                 if(o.y){
6021                     me.setStyle(OVERFLOWY, o.y);
6022                 }
6023             }
6024             return me;
6025         },
6026
6027         // private
6028         addStyles : function(sides, styles){
6029             var val = 0,
6030                 m = sides.match(/\w/g),
6031                 s;
6032             for (var i=0, len=m.length; i<len; i++) {
6033                 s = m[i] && parseInt(this.getStyle(styles[m[i]]), 10);
6034                 if (s) {
6035                     val += MATH.abs(s);
6036                 }
6037             }
6038             return val;
6039         },
6040
6041         margins : margins
6042     }
6043 }()
6044 );
6045 /**\r
6046  * @class Ext.Element\r
6047  */\r
6048 \r
6049 // special markup used throughout Ext when box wrapping elements\r
6050 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
6051 \r
6052 Ext.Element.addMethods(function(){\r
6053     var INTERNAL = "_internal",\r
6054         pxMatch = /(\d+)px/;\r
6055     return {\r
6056         /**\r
6057          * More flexible version of {@link #setStyle} for setting style properties.\r
6058          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or\r
6059          * a function which returns such a specification.\r
6060          * @return {Ext.Element} this\r
6061          */\r
6062         applyStyles : function(style){\r
6063             Ext.DomHelper.applyStyles(this.dom, style);\r
6064             return this;\r
6065         },\r
6066 \r
6067         /**\r
6068          * Returns an object with properties matching the styles requested.\r
6069          * For example, el.getStyles('color', 'font-size', 'width') might return\r
6070          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.\r
6071          * @param {String} style1 A style name\r
6072          * @param {String} style2 A style name\r
6073          * @param {String} etc.\r
6074          * @return {Object} The style object\r
6075          */\r
6076         getStyles : function(){\r
6077             var ret = {};\r
6078             Ext.each(arguments, function(v) {\r
6079                ret[v] = this.getStyle(v);\r
6080             },\r
6081             this);\r
6082             return ret;\r
6083         },\r
6084 \r
6085         // deprecated\r
6086         getStyleSize : function(){\r
6087             var me = this,\r
6088                 w,\r
6089                 h,\r
6090                 d = this.dom,\r
6091                 s = d.style;\r
6092             if(s.width && s.width != 'auto'){\r
6093                 w = parseInt(s.width, 10);\r
6094                 if(me.isBorderBox()){\r
6095                    w -= me.getFrameWidth('lr');\r
6096                 }\r
6097             }\r
6098             if(s.height && s.height != 'auto'){\r
6099                 h = parseInt(s.height, 10);\r
6100                 if(me.isBorderBox()){\r
6101                    h -= me.getFrameWidth('tb');\r
6102                 }\r
6103             }\r
6104             return {width: w || me.getWidth(true), height: h || me.getHeight(true)};\r
6105         },\r
6106 \r
6107         // private  ==> used by ext full\r
6108         setOverflow : function(v){\r
6109             var dom = this.dom;\r
6110             if(v=='auto' && Ext.isMac && Ext.isGecko2){ // work around stupid FF 2.0/Mac scroll bar bug\r
6111                 dom.style.overflow = 'hidden';\r
6112                 (function(){dom.style.overflow = 'auto';}).defer(1);\r
6113             }else{\r
6114                 dom.style.overflow = v;\r
6115             }\r
6116         },\r
6117 \r
6118        /**\r
6119         * <p>Wraps the specified element with a special 9 element markup/CSS block that renders by default as\r
6120         * a gray container with a gradient background, rounded corners and a 4-way shadow.</p>\r
6121         * <p>This special markup is used throughout Ext when box wrapping elements ({@link Ext.Button},\r
6122         * {@link Ext.Panel} when <tt>{@link Ext.Panel#frame frame=true}</tt>, {@link Ext.Window}).  The markup\r
6123         * is of this form:</p>\r
6124         * <pre><code>\r
6125     Ext.Element.boxMarkup =\r
6126     &#39;&lt;div class="{0}-tl">&lt;div class="{0}-tr">&lt;div class="{0}-tc">&lt;/div>&lt;/div>&lt;/div>\r
6127      &lt;div class="{0}-ml">&lt;div class="{0}-mr">&lt;div class="{0}-mc">&lt;/div>&lt;/div>&lt;/div>\r
6128      &lt;div class="{0}-bl">&lt;div class="{0}-br">&lt;div class="{0}-bc">&lt;/div>&lt;/div>&lt;/div>&#39;;\r
6129         * </code></pre>\r
6130         * <p>Example usage:</p>\r
6131         * <pre><code>\r
6132     // Basic box wrap\r
6133     Ext.get("foo").boxWrap();\r
6134 \r
6135     // You can also add a custom class and use CSS inheritance rules to customize the box look.\r
6136     // 'x-box-blue' is a built-in alternative -- look at the related CSS definitions as an example\r
6137     // for how to create a custom box wrap style.\r
6138     Ext.get("foo").boxWrap().addClass("x-box-blue");\r
6139         * </code></pre>\r
6140         * @param {String} class (optional) A base CSS class to apply to the containing wrapper element\r
6141         * (defaults to <tt>'x-box'</tt>). Note that there are a number of CSS rules that are dependent on\r
6142         * this name to make the overall effect work, so if you supply an alternate base class, make sure you\r
6143         * also supply all of the necessary rules.\r
6144         * @return {Ext.Element} The outermost wrapping element of the created box structure.\r
6145         */\r
6146         boxWrap : function(cls){\r
6147             cls = cls || 'x-box';\r
6148             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
6149             Ext.DomQuery.selectNode('.' + cls + '-mc', el.dom).appendChild(this.dom);\r
6150             return el;\r
6151         },\r
6152 \r
6153         /**\r
6154          * Set the size of this Element. If animation is true, both width and height will be animated concurrently.\r
6155          * @param {Mixed} width The new width. This may be one of:<div class="mdetail-params"><ul>\r
6156          * <li>A Number specifying the new width in this Element's {@link #defaultUnit}s (by default, pixels).</li>\r
6157          * <li>A String used to set the CSS width style. Animation may <b>not</b> be used.\r
6158          * <li>A size object in the format <code>{width: widthValue, height: heightValue}</code>.</li>\r
6159          * </ul></div>\r
6160          * @param {Mixed} height The new height. This may be one of:<div class="mdetail-params"><ul>\r
6161          * <li>A Number specifying the new height in this Element's {@link #defaultUnit}s (by default, pixels).</li>\r
6162          * <li>A String used to set the CSS height style. Animation may <b>not</b> be used.</li>\r
6163          * </ul></div>\r
6164          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object\r
6165          * @return {Ext.Element} this\r
6166          */\r
6167         setSize : function(width, height, animate){\r
6168             var me = this;\r
6169             if(Ext.isObject(width)){ // in case of object from getSize()\r
6170                 height = width.height;\r
6171                 width = width.width;\r
6172             }\r
6173             width = me.adjustWidth(width);\r
6174             height = me.adjustHeight(height);\r
6175             if(!animate || !me.anim){\r
6176                 me.dom.style.width = me.addUnits(width);\r
6177                 me.dom.style.height = me.addUnits(height);\r
6178             }else{\r
6179                 me.anim({width: {to: width}, height: {to: height}}, me.preanim(arguments, 2));\r
6180             }\r
6181             return me;\r
6182         },\r
6183 \r
6184         /**\r
6185          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders\r
6186          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements\r
6187          * if a height has not been set using CSS.\r
6188          * @return {Number}\r
6189          */\r
6190         getComputedHeight : function(){\r
6191             var me = this,\r
6192                 h = Math.max(me.dom.offsetHeight, me.dom.clientHeight);\r
6193             if(!h){\r
6194                 h = parseInt(me.getStyle('height'), 10) || 0;\r
6195                 if(!me.isBorderBox()){\r
6196                     h += me.getFrameWidth('tb');\r
6197                 }\r
6198             }\r
6199             return h;\r
6200         },\r
6201 \r
6202         /**\r
6203          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders\r
6204          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements\r
6205          * if a width has not been set using CSS.\r
6206          * @return {Number}\r
6207          */\r
6208         getComputedWidth : function(){\r
6209             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);\r
6210             if(!w){\r
6211                 w = parseInt(this.getStyle('width'), 10) || 0;\r
6212                 if(!this.isBorderBox()){\r
6213                     w += this.getFrameWidth('lr');\r
6214                 }\r
6215             }\r
6216             return w;\r
6217         },\r
6218 \r
6219         /**\r
6220          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()\r
6221          for more information about the sides.\r
6222          * @param {String} sides\r
6223          * @return {Number}\r
6224          */\r
6225         getFrameWidth : function(sides, onlyContentBox){\r
6226             return onlyContentBox && this.isBorderBox() ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));\r
6227         },\r
6228 \r
6229         /**\r
6230          * Sets up event handlers to add and remove a css class when the mouse is over this element\r
6231          * @param {String} className\r
6232          * @return {Ext.Element} this\r
6233          */\r
6234         addClassOnOver : function(className){\r
6235             this.hover(\r
6236                 function(){\r
6237                     Ext.fly(this, INTERNAL).addClass(className);\r
6238                 },\r
6239                 function(){\r
6240                     Ext.fly(this, INTERNAL).removeClass(className);\r
6241                 }\r
6242             );\r
6243             return this;\r
6244         },\r
6245 \r
6246         /**\r
6247          * Sets up event handlers to add and remove a css class when this element has the focus\r
6248          * @param {String} className\r
6249          * @return {Ext.Element} this\r
6250          */\r
6251         addClassOnFocus : function(className){\r
6252             this.on("focus", function(){\r
6253                 Ext.fly(this, INTERNAL).addClass(className);\r
6254             }, this.dom);\r
6255             this.on("blur", function(){\r
6256                 Ext.fly(this, INTERNAL).removeClass(className);\r
6257             }, this.dom);\r
6258             return this;\r
6259         },\r
6260 \r
6261         /**\r
6262          * 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
6263          * @param {String} className\r
6264          * @return {Ext.Element} this\r
6265          */\r
6266         addClassOnClick : function(className){\r
6267             var dom = this.dom;\r
6268             this.on("mousedown", function(){\r
6269                 Ext.fly(dom, INTERNAL).addClass(className);\r
6270                 var d = Ext.getDoc(),\r
6271                     fn = function(){\r
6272                         Ext.fly(dom, INTERNAL).removeClass(className);\r
6273                         d.removeListener("mouseup", fn);\r
6274                     };\r
6275                 d.on("mouseup", fn);\r
6276             });\r
6277             return this;\r
6278         },\r
6279 \r
6280         /**\r
6281          * <p>Returns the dimensions of the element available to lay content out in.<p>\r
6282          * <p>If the element (or any ancestor element) has CSS style <code>display : none</code>, the dimensions will be zero.</p>\r
6283          * example:<pre><code>\r
6284         var vpSize = Ext.getBody().getViewSize();\r
6285 \r
6286         // all Windows created afterwards will have a default value of 90% height and 95% width\r
6287         Ext.Window.override({\r
6288             width: vpSize.width * 0.9,\r
6289             height: vpSize.height * 0.95\r
6290         });\r
6291         // To handle window resizing you would have to hook onto onWindowResize.\r
6292         </code></pre>\r
6293          * @param {Boolean} contentBox True to return the W3 content box <i>within</i> the padding area of the element. False\r
6294          * or omitted to return the full area of the element within the border. See <a href="http://www.w3.org/TR/CSS2/box.html">http://www.w3.org/TR/CSS2/box.html</a>\r
6295          * @return {Object} An object containing the elements's area: <code>{width: &lt;element width>, height: &lt;element height>}</code>\r
6296          */\r
6297         getViewSize : function(contentBox){\r
6298             var doc = document,\r
6299                 me = this,\r
6300                 d = me.dom,\r
6301                 extdom = Ext.lib.Dom,\r
6302                 isDoc = (d == doc || d == doc.body),\r
6303                 isBB, w, h, tbBorder = 0, lrBorder = 0,\r
6304                 tbPadding = 0, lrPadding = 0;\r
6305             if (isDoc) {\r
6306                 return { width: extdom.getViewWidth(), height: extdom.getViewHeight() };\r
6307             }\r
6308             isBB = me.isBorderBox();\r
6309             tbBorder = me.getBorderWidth('tb');\r
6310             lrBorder = me.getBorderWidth('lr');\r
6311             tbPadding = me.getPadding('tb');\r
6312             lrPadding = me.getPadding('lr');\r
6313 \r
6314             // Width calcs\r
6315             // Try the style first, then clientWidth, then offsetWidth\r
6316             if (w = me.getStyle('width').match(pxMatch)){\r
6317                 if ((w = parseInt(w[1], 10)) && isBB){\r
6318                     // Style includes the padding and border if isBB\r
6319                     w -= (lrBorder + lrPadding);\r
6320                 }\r
6321                 if (!contentBox){\r
6322                     w += lrPadding;\r
6323                 }\r
6324             } else {\r
6325                 if (!(w = d.clientWidth) && (w = d.offsetWidth)){\r
6326                     w -= lrBorder;\r
6327                 }\r
6328                 if (w && contentBox){\r
6329                     w -= lrPadding;\r
6330                 }\r
6331             }\r
6332 \r
6333             // Height calcs\r
6334             // Try the style first, then clientHeight, then offsetHeight\r
6335             if (h = me.getStyle('height').match(pxMatch)){\r
6336                 if ((h = parseInt(h[1], 10)) && isBB){\r
6337                     // Style includes the padding and border if isBB\r
6338                     h -= (tbBorder + tbPadding);\r
6339                 }\r
6340                 if (!contentBox){\r
6341                     h += tbPadding;\r
6342                 }\r
6343             } else {\r
6344                 if (!(h = d.clientHeight) && (h = d.offsetHeight)){\r
6345                     h -= tbBorder;\r
6346                 }\r
6347                 if (h && contentBox){\r
6348                     h -= tbPadding;\r
6349                 }\r
6350             }\r
6351 \r
6352             return {\r
6353                 width : w,\r
6354                 height : h\r
6355             };\r
6356         },\r
6357 \r
6358         /**\r
6359          * Returns the size of the element.\r
6360          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding\r
6361          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}\r
6362          */\r
6363         getSize : function(contentSize){\r
6364             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};\r
6365         },\r
6366 \r
6367         /**\r
6368          * Forces the browser to repaint this element\r
6369          * @return {Ext.Element} this\r
6370          */\r
6371         repaint : function(){\r
6372             var dom = this.dom;\r
6373             this.addClass("x-repaint");\r
6374             setTimeout(function(){\r
6375                 Ext.fly(dom).removeClass("x-repaint");\r
6376             }, 1);\r
6377             return this;\r
6378         },\r
6379 \r
6380         /**\r
6381          * Disables text selection for this element (normalized across browsers)\r
6382          * @return {Ext.Element} this\r
6383          */\r
6384         unselectable : function(){\r
6385             this.dom.unselectable = "on";\r
6386             return this.swallowEvent("selectstart", true).\r
6387                         applyStyles("-moz-user-select:none;-khtml-user-select:none;").\r
6388                         addClass("x-unselectable");\r
6389         },\r
6390 \r
6391         /**\r
6392          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,\r
6393          * then it returns the calculated width of the sides (see getPadding)\r
6394          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides\r
6395          * @return {Object/Number}\r
6396          */\r
6397         getMargins : function(side){\r
6398             var me = this,\r
6399                 key,\r
6400                 hash = {t:"top", l:"left", r:"right", b: "bottom"},\r
6401                 o = {};\r
6402 \r
6403             if (!side) {\r
6404                 for (key in me.margins){\r
6405                     o[hash[key]] = parseInt(me.getStyle(me.margins[key]), 10) || 0;\r
6406                 }\r
6407                 return o;\r
6408             } else {\r
6409                 return me.addStyles.call(me, side, me.margins);\r
6410             }\r
6411         }\r
6412     };\r
6413 }());\r
6414 /**\r
6415  * @class Ext.Element\r
6416  */\r
6417 (function(){\r
6418 var D = Ext.lib.Dom,\r
6419         LEFT = "left",\r
6420         RIGHT = "right",\r
6421         TOP = "top",\r
6422         BOTTOM = "bottom",\r
6423         POSITION = "position",\r
6424         STATIC = "static",\r
6425         RELATIVE = "relative",\r
6426         AUTO = "auto",\r
6427         ZINDEX = "z-index";\r
6428 \r
6429 Ext.Element.addMethods({\r
6430         /**\r
6431       * 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
6432       * @return {Number} The X position of the element\r
6433       */\r
6434     getX : function(){\r
6435         return D.getX(this.dom);\r
6436     },\r
6437 \r
6438     /**\r
6439       * 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
6440       * @return {Number} The Y position of the element\r
6441       */\r
6442     getY : function(){\r
6443         return D.getY(this.dom);\r
6444     },\r
6445 \r
6446     /**\r
6447       * 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
6448       * @return {Array} The XY position of the element\r
6449       */\r
6450     getXY : function(){\r
6451         return D.getXY(this.dom);\r
6452     },\r
6453 \r
6454     /**\r
6455       * 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
6456       * @param {Mixed} element The element to get the offsets from.\r
6457       * @return {Array} The XY page offsets (e.g. [100, -200])\r
6458       */\r
6459     getOffsetsTo : function(el){\r
6460         var o = this.getXY(),\r
6461                 e = Ext.fly(el, '_internal').getXY();\r
6462         return [o[0]-e[0],o[1]-e[1]];\r
6463     },\r
6464 \r
6465     /**\r
6466      * 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
6467      * @param {Number} The X position of the element\r
6468      * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object\r
6469      * @return {Ext.Element} this\r
6470      */\r
6471     setX : function(x, animate){            \r
6472             return this.setXY([x, this.getY()], this.animTest(arguments, animate, 1));\r
6473     },\r
6474 \r
6475     /**\r
6476      * 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
6477      * @param {Number} The Y position of the element\r
6478      * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object\r
6479      * @return {Ext.Element} this\r
6480      */\r
6481     setY : function(y, animate){            \r
6482             return this.setXY([this.getX(), y], this.animTest(arguments, animate, 1));\r
6483     },\r
6484 \r
6485     /**\r
6486      * Sets the element's left position directly using CSS style (instead of {@link #setX}).\r
6487      * @param {String} left The left CSS property value\r
6488      * @return {Ext.Element} this\r
6489      */\r
6490     setLeft : function(left){\r
6491         this.setStyle(LEFT, this.addUnits(left));\r
6492         return this;\r
6493     },\r
6494 \r
6495     /**\r
6496      * Sets the element's top position directly using CSS style (instead of {@link #setY}).\r
6497      * @param {String} top The top CSS property value\r
6498      * @return {Ext.Element} this\r
6499      */\r
6500     setTop : function(top){\r
6501         this.setStyle(TOP, this.addUnits(top));\r
6502         return this;\r
6503     },\r
6504 \r
6505     /**\r
6506      * Sets the element's CSS right style.\r
6507      * @param {String} right The right CSS property value\r
6508      * @return {Ext.Element} this\r
6509      */\r
6510     setRight : function(right){\r
6511         this.setStyle(RIGHT, this.addUnits(right));\r
6512         return this;\r
6513     },\r
6514 \r
6515     /**\r
6516      * Sets the element's CSS bottom style.\r
6517      * @param {String} bottom The bottom CSS property value\r
6518      * @return {Ext.Element} this\r
6519      */\r
6520     setBottom : function(bottom){\r
6521         this.setStyle(BOTTOM, this.addUnits(bottom));\r
6522         return this;\r
6523     },\r
6524 \r
6525     /**\r
6526      * Sets the position of the element in page coordinates, regardless of how the element is positioned.\r
6527      * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).\r
6528      * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)\r
6529      * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object\r
6530      * @return {Ext.Element} this\r
6531      */\r
6532     setXY : function(pos, animate){\r
6533             var me = this;\r
6534         if(!animate || !me.anim){\r
6535             D.setXY(me.dom, pos);\r
6536         }else{\r
6537             me.anim({points: {to: pos}}, me.preanim(arguments, 1), 'motion');\r
6538         }\r
6539         return me;\r
6540     },\r
6541 \r
6542     /**\r
6543      * Sets the position of the element in page coordinates, regardless of how the element is positioned.\r
6544      * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).\r
6545      * @param {Number} x X value for new position (coordinates are page-based)\r
6546      * @param {Number} y Y value for new position (coordinates are page-based)\r
6547      * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object\r
6548      * @return {Ext.Element} this\r
6549      */\r
6550     setLocation : function(x, y, animate){\r
6551         return this.setXY([x, y], this.animTest(arguments, animate, 2));\r
6552     },\r
6553 \r
6554     /**\r
6555      * Sets the position of the element in page coordinates, regardless of how the element is positioned.\r
6556      * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).\r
6557      * @param {Number} x X value for new position (coordinates are page-based)\r
6558      * @param {Number} y Y value for new position (coordinates are page-based)\r
6559      * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object\r
6560      * @return {Ext.Element} this\r
6561      */\r
6562     moveTo : function(x, y, animate){\r
6563         return this.setXY([x, y], this.animTest(arguments, animate, 2));        \r
6564     },    \r
6565     \r
6566     /**\r
6567      * Gets the left X coordinate\r
6568      * @param {Boolean} local True to get the local css position instead of page coordinate\r
6569      * @return {Number}\r
6570      */\r
6571     getLeft : function(local){\r
6572             return !local ? this.getX() : parseInt(this.getStyle(LEFT), 10) || 0;\r
6573     },\r
6574 \r
6575     /**\r
6576      * Gets the right X coordinate of the element (element X position + element width)\r
6577      * @param {Boolean} local True to get the local css position instead of page coordinate\r
6578      * @return {Number}\r
6579      */\r
6580     getRight : function(local){\r
6581             var me = this;\r
6582             return !local ? me.getX() + me.getWidth() : (me.getLeft(true) + me.getWidth()) || 0;\r
6583     },\r
6584 \r
6585     /**\r
6586      * Gets the top Y coordinate\r
6587      * @param {Boolean} local True to get the local css position instead of page coordinate\r
6588      * @return {Number}\r
6589      */\r
6590     getTop : function(local) {\r
6591             return !local ? this.getY() : parseInt(this.getStyle(TOP), 10) || 0;\r
6592     },\r
6593 \r
6594     /**\r
6595      * Gets the bottom Y coordinate of the element (element Y position + element height)\r
6596      * @param {Boolean} local True to get the local css position instead of page coordinate\r
6597      * @return {Number}\r
6598      */\r
6599     getBottom : function(local){\r
6600             var me = this;\r
6601             return !local ? me.getY() + me.getHeight() : (me.getTop(true) + me.getHeight()) || 0;\r
6602     },\r
6603 \r
6604     /**\r
6605     * Initializes positioning on this element. If a desired position is not passed, it will make the\r
6606     * the element positioned relative IF it is not already positioned.\r
6607     * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"\r
6608     * @param {Number} zIndex (optional) The zIndex to apply\r
6609     * @param {Number} x (optional) Set the page X position\r
6610     * @param {Number} y (optional) Set the page Y position\r
6611     */\r
6612     position : function(pos, zIndex, x, y){\r
6613             var me = this;\r
6614             \r
6615         if(!pos && me.isStyle(POSITION, STATIC)){           \r
6616             me.setStyle(POSITION, RELATIVE);           \r
6617         } else if(pos) {\r
6618             me.setStyle(POSITION, pos);\r
6619         }\r
6620         if(zIndex){\r
6621             me.setStyle(ZINDEX, zIndex);\r
6622         }\r
6623         if(x || y) me.setXY([x || false, y || false]);\r
6624     },\r
6625 \r
6626     /**\r
6627     * Clear positioning back to the default when the document was loaded\r
6628     * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.\r
6629     * @return {Ext.Element} this\r
6630      */\r
6631     clearPositioning : function(value){\r
6632         value = value || '';\r
6633         this.setStyle({\r
6634             left : value,\r
6635             right : value,\r
6636             top : value,\r
6637             bottom : value,\r
6638             "z-index" : "",\r
6639             position : STATIC\r
6640         });\r
6641         return this;\r
6642     },\r
6643 \r
6644     /**\r
6645     * Gets an object with all CSS positioning properties. Useful along with setPostioning to get\r
6646     * snapshot before performing an update and then restoring the element.\r
6647     * @return {Object}\r
6648     */\r
6649     getPositioning : function(){\r
6650         var l = this.getStyle(LEFT);\r
6651         var t = this.getStyle(TOP);\r
6652         return {\r
6653             "position" : this.getStyle(POSITION),\r
6654             "left" : l,\r
6655             "right" : l ? "" : this.getStyle(RIGHT),\r
6656             "top" : t,\r
6657             "bottom" : t ? "" : this.getStyle(BOTTOM),\r
6658             "z-index" : this.getStyle(ZINDEX)\r
6659         };\r
6660     },\r
6661     \r
6662     /**\r
6663     * Set positioning with an object returned by getPositioning().\r
6664     * @param {Object} posCfg\r
6665     * @return {Ext.Element} this\r
6666      */\r
6667     setPositioning : function(pc){\r
6668             var me = this,\r
6669                 style = me.dom.style;\r
6670                 \r
6671         me.setStyle(pc);\r
6672         \r
6673         if(pc.right == AUTO){\r
6674             style.right = "";\r
6675         }\r
6676         if(pc.bottom == AUTO){\r
6677             style.bottom = "";\r
6678         }\r
6679         \r
6680         return me;\r
6681     },    \r
6682         \r
6683     /**\r
6684      * Translates the passed page coordinates into left/top css values for this element\r
6685      * @param {Number/Array} x The page x or an array containing [x, y]\r
6686      * @param {Number} y (optional) The page y, required if x is not an array\r
6687      * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}\r
6688      */\r
6689     translatePoints : function(x, y){                \r
6690             y = isNaN(x[1]) ? y : x[1];\r
6691         x = isNaN(x[0]) ? x : x[0];\r
6692         var me = this,\r
6693                 relative = me.isStyle(POSITION, RELATIVE),\r
6694                 o = me.getXY(),\r
6695                 l = parseInt(me.getStyle(LEFT), 10),\r
6696                 t = parseInt(me.getStyle(TOP), 10);\r
6697         \r
6698         l = !isNaN(l) ? l : (relative ? 0 : me.dom.offsetLeft);\r
6699         t = !isNaN(t) ? t : (relative ? 0 : me.dom.offsetTop);        \r
6700 \r
6701         return {left: (x - o[0] + l), top: (y - o[1] + t)}; \r
6702     },\r
6703     \r
6704     animTest : function(args, animate, i) {\r
6705         return !!animate && this.preanim ? this.preanim(args, i) : false;\r
6706     }\r
6707 });\r
6708 })();/**\r
6709  * @class Ext.Element\r
6710  */\r
6711 Ext.Element.addMethods({\r
6712     /**\r
6713      * 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
6714      * @param {Object} box The box to fill {x, y, width, height}\r
6715      * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically\r
6716      * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object\r
6717      * @return {Ext.Element} this\r
6718      */\r
6719     setBox : function(box, adjust, animate){\r
6720         var me = this,\r
6721                 w = box.width, \r
6722                 h = box.height;\r
6723         if((adjust && !me.autoBoxAdjust) && !me.isBorderBox()){\r
6724            w -= (me.getBorderWidth("lr") + me.getPadding("lr"));\r
6725            h -= (me.getBorderWidth("tb") + me.getPadding("tb"));\r
6726         }\r
6727         me.setBounds(box.x, box.y, w, h, me.animTest.call(me, arguments, animate, 2));\r
6728         return me;\r
6729     },\r
6730 \r
6731     /**\r
6732      * Return an object defining the area of this Element which can be passed to {@link #setBox} to\r
6733      * set another Element's size/location to match this element.\r
6734      * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.\r
6735      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.\r
6736      * @return {Object} box An object in the format<pre><code>\r
6737 {\r
6738     x: &lt;Element's X position>,\r
6739     y: &lt;Element's Y position>,\r
6740     width: &lt;Element's width>,\r
6741     height: &lt;Element's height>,\r
6742     bottom: &lt;Element's lower bound>,\r
6743     right: &lt;Element's rightmost bound>\r
6744 }\r
6745 </code></pre>\r
6746      * The returned object may also be addressed as an Array where index 0 contains the X position\r
6747      * and index 1 contains the Y position. So the result may also be used for {@link #setXY}\r
6748      */\r
6749         getBox : function(contentBox, local) {      \r
6750             var me = this,\r
6751                 xy,\r
6752                 left,\r
6753                 top,\r
6754                 getBorderWidth = me.getBorderWidth,\r
6755                 getPadding = me.getPadding, \r
6756                 l,\r
6757                 r,\r
6758                 t,\r
6759                 b;\r
6760         if(!local){\r
6761             xy = me.getXY();\r
6762         }else{\r
6763             left = parseInt(me.getStyle("left"), 10) || 0;\r
6764             top = parseInt(me.getStyle("top"), 10) || 0;\r
6765             xy = [left, top];\r
6766         }\r
6767         var el = me.dom, w = el.offsetWidth, h = el.offsetHeight, bx;\r
6768         if(!contentBox){\r
6769             bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};\r
6770         }else{\r
6771             l = getBorderWidth.call(me, "l") + getPadding.call(me, "l");\r
6772             r = getBorderWidth.call(me, "r") + getPadding.call(me, "r");\r
6773             t = getBorderWidth.call(me, "t") + getPadding.call(me, "t");\r
6774             b = getBorderWidth.call(me, "b") + getPadding.call(me, "b");\r
6775             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
6776         }\r
6777         bx.right = bx.x + bx.width;\r
6778         bx.bottom = bx.y + bx.height;\r
6779         return bx;\r
6780         },\r
6781         \r
6782     /**\r
6783      * Move this element relative to its current position.\r
6784      * @param {String} direction Possible values are: "l" (or "left"), "r" (or "right"), "t" (or "top", or "up"), "b" (or "bottom", or "down").\r
6785      * @param {Number} distance How far to move the element in pixels\r
6786      * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object\r
6787      * @return {Ext.Element} this\r
6788      */\r
6789      move : function(direction, distance, animate){\r
6790         var me = this,          \r
6791                 xy = me.getXY(),\r
6792                 x = xy[0],\r
6793                 y = xy[1],              \r
6794                 left = [x - distance, y],\r
6795                 right = [x + distance, y],\r
6796                 top = [x, y - distance],\r
6797                 bottom = [x, y + distance],\r
6798                 hash = {\r
6799                         l :     left,\r
6800                         left : left,\r
6801                         r : right,\r
6802                         right : right,\r
6803                         t : top,\r
6804                         top : top,\r
6805                         up : top,\r
6806                         b : bottom, \r
6807                         bottom : bottom,\r
6808                         down : bottom                           \r
6809                 };\r
6810         \r
6811             direction = direction.toLowerCase();    \r
6812             me.moveTo(hash[direction][0], hash[direction][1], me.animTest.call(me, arguments, animate, 2));\r
6813     },\r
6814     \r
6815     /**\r
6816      * Quick set left and top adding default units\r
6817      * @param {String} left The left CSS property value\r
6818      * @param {String} top The top CSS property value\r
6819      * @return {Ext.Element} this\r
6820      */\r
6821      setLeftTop : function(left, top){\r
6822             var me = this,\r
6823                 style = me.dom.style;\r
6824         style.left = me.addUnits(left);\r
6825         style.top = me.addUnits(top);\r
6826         return me;\r
6827     },\r
6828     \r
6829     /**\r
6830      * Returns the region of the given element.\r
6831      * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).\r
6832      * @return {Region} A Ext.lib.Region containing "top, left, bottom, right" member data.\r
6833      */\r
6834     getRegion : function(){\r
6835         return Ext.lib.Dom.getRegion(this.dom);\r
6836     },\r
6837     \r
6838     /**\r
6839      * 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
6840      * @param {Number} x X value for new position (coordinates are page-based)\r
6841      * @param {Number} y Y value for new position (coordinates are page-based)\r
6842      * @param {Mixed} width The new width. This may be one of:<div class="mdetail-params"><ul>\r
6843      * <li>A Number specifying the new width in this Element's {@link #defaultUnit}s (by default, pixels)</li>\r
6844      * <li>A String used to set the CSS width style. Animation may <b>not</b> be used.\r
6845      * </ul></div>\r
6846      * @param {Mixed} height The new height. This may be one of:<div class="mdetail-params"><ul>\r
6847      * <li>A Number specifying the new height in this Element's {@link #defaultUnit}s (by default, pixels)</li>\r
6848      * <li>A String used to set the CSS height style. Animation may <b>not</b> be used.</li>\r
6849      * </ul></div>\r
6850      * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object\r
6851      * @return {Ext.Element} this\r
6852      */\r
6853     setBounds : function(x, y, width, height, animate){\r
6854             var me = this;\r
6855         if (!animate || !me.anim) {\r
6856             me.setSize(width, height);\r
6857             me.setLocation(x, y);\r
6858         } else {\r
6859             me.anim({points: {to: [x, y]}, \r
6860                          width: {to: me.adjustWidth(width)}, \r
6861                          height: {to: me.adjustHeight(height)}},\r
6862                      me.preanim(arguments, 4), \r
6863                      'motion');\r
6864         }\r
6865         return me;\r
6866     },\r
6867 \r
6868     /**\r
6869      * 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
6870      * @param {Ext.lib.Region} region The region to fill\r
6871      * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object\r
6872      * @return {Ext.Element} this\r
6873      */\r
6874     setRegion : function(region, animate) {\r
6875         return this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.animTest.call(this, arguments, animate, 1));\r
6876     }\r
6877 });/**\r
6878  * @class Ext.Element\r
6879  */\r
6880 Ext.Element.addMethods({\r
6881     /**\r
6882      * Returns true if this element is scrollable.\r
6883      * @return {Boolean}\r
6884      */\r
6885     isScrollable : function(){\r
6886         var dom = this.dom;\r
6887         return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;\r
6888     },\r
6889 \r
6890     /**\r
6891      * 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
6892      * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.\r
6893      * @param {Number} value The new scroll value.\r
6894      * @return {Element} this\r
6895      */\r
6896     scrollTo : function(side, value){\r
6897         this.dom["scroll" + (/top/i.test(side) ? "Top" : "Left")] = value;\r
6898         return this;\r
6899     },\r
6900 \r
6901     /**\r
6902      * Returns the current scroll position of the element.\r
6903      * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}\r
6904      */\r
6905     getScroll : function(){\r
6906         var d = this.dom, \r
6907             doc = document,\r
6908             body = doc.body,\r
6909             docElement = doc.documentElement,\r
6910             l,\r
6911             t,\r
6912             ret;\r
6913 \r
6914         if(d == doc || d == body){\r
6915             if(Ext.isIE && Ext.isStrict){\r
6916                 l = docElement.scrollLeft; \r
6917                 t = docElement.scrollTop;\r
6918             }else{\r
6919                 l = window.pageXOffset;\r
6920                 t = window.pageYOffset;\r
6921             }\r
6922             ret = {left: l || (body ? body.scrollLeft : 0), top: t || (body ? body.scrollTop : 0)};\r
6923         }else{\r
6924             ret = {left: d.scrollLeft, top: d.scrollTop};\r
6925         }\r
6926         return ret;\r
6927     }\r
6928 });/**\r
6929  * @class Ext.Element\r
6930  */\r
6931 Ext.Element.addMethods({\r
6932     /**\r
6933      * 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
6934      * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.\r
6935      * @param {Number} value The new scroll value\r
6936      * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object\r
6937      * @return {Element} this\r
6938      */\r
6939     scrollTo : function(side, value, animate){\r
6940         var top = /top/i.test(side), //check if we're scrolling top or left\r
6941                 me = this,\r
6942                 dom = me.dom,\r
6943             prop;\r
6944         if (!animate || !me.anim) {\r
6945             prop = 'scroll' + (top ? 'Top' : 'Left'), // just setting the value, so grab the direction\r
6946             dom[prop] = value;\r
6947         }else{\r
6948             prop = 'scroll' + (top ? 'Left' : 'Top'), // if scrolling top, we need to grab scrollLeft, if left, scrollTop\r
6949             me.anim({scroll: {to: top ? [dom[prop], value] : [value, dom[prop]]}},\r
6950                          me.preanim(arguments, 2), 'scroll');\r
6951         }\r
6952         return me;\r
6953     },\r
6954     \r
6955     /**\r
6956      * Scrolls this element into view within the passed container.\r
6957      * @param {Mixed} container (optional) The container element to scroll (defaults to document.body).  Should be a\r
6958      * string (id), dom node, or Ext.Element.\r
6959      * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)\r
6960      * @return {Ext.Element} this\r
6961      */\r
6962     scrollIntoView : function(container, hscroll){\r
6963         var c = Ext.getDom(container) || Ext.getBody().dom,\r
6964                 el = this.dom,\r
6965                 o = this.getOffsetsTo(c),\r
6966             l = o[0] + c.scrollLeft,\r
6967             t = o[1] + c.scrollTop,\r
6968             b = t + el.offsetHeight,\r
6969             r = l + el.offsetWidth,\r
6970                 ch = c.clientHeight,\r
6971                 ct = parseInt(c.scrollTop, 10),\r
6972                 cl = parseInt(c.scrollLeft, 10),\r
6973                 cb = ct + ch,\r
6974                 cr = cl + c.clientWidth;\r
6975 \r
6976         if (el.offsetHeight > ch || t < ct) {\r
6977                 c.scrollTop = t;\r
6978         } else if (b > cb){\r
6979             c.scrollTop = b-ch;\r
6980         }\r
6981         c.scrollTop = c.scrollTop; // corrects IE, other browsers will ignore\r
6982 \r
6983         if(hscroll !== false){\r
6984                         if(el.offsetWidth > c.clientWidth || l < cl){\r
6985                 c.scrollLeft = l;\r
6986             }else if(r > cr){\r
6987                 c.scrollLeft = r - c.clientWidth;\r
6988             }\r
6989             c.scrollLeft = c.scrollLeft;\r
6990         }\r
6991         return this;\r
6992     },\r
6993 \r
6994     // private\r
6995     scrollChildIntoView : function(child, hscroll){\r
6996         Ext.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);\r
6997     },\r
6998     \r
6999     /**\r
7000      * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is\r
7001      * within this element's scrollable range.\r
7002      * @param {String} direction Possible values are: "l" (or "left"), "r" (or "right"), "t" (or "top", or "up"), "b" (or "bottom", or "down").\r
7003      * @param {Number} distance How far to scroll the element in pixels\r
7004      * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object\r
7005      * @return {Boolean} Returns true if a scroll was triggered or false if the element\r
7006      * was scrolled as far as it could go.\r
7007      */\r
7008      scroll : function(direction, distance, animate){\r
7009          if(!this.isScrollable()){\r
7010              return;\r
7011          }\r
7012          var el = this.dom,\r
7013             l = el.scrollLeft, t = el.scrollTop,\r
7014             w = el.scrollWidth, h = el.scrollHeight,\r
7015             cw = el.clientWidth, ch = el.clientHeight,\r
7016             scrolled = false, v,\r
7017             hash = {\r
7018                 l: Math.min(l + distance, w-cw),\r
7019                 r: v = Math.max(l - distance, 0),\r
7020                 t: Math.max(t - distance, 0),\r
7021                 b: Math.min(t + distance, h-ch)\r
7022             };\r
7023             hash.d = hash.b;\r
7024             hash.u = hash.t;\r
7025             \r
7026          direction = direction.substr(0, 1);\r
7027          if((v = hash[direction]) > -1){\r
7028             scrolled = true;\r
7029             this.scrollTo(direction == 'l' || direction == 'r' ? 'left' : 'top', v, this.preanim(arguments, 2));\r
7030          }\r
7031          return scrolled;\r
7032     }\r
7033 });/**\r
7034  * @class Ext.Element\r
7035  */\r
7036 /**\r
7037  * Visibility mode constant for use with {@link #setVisibilityMode}. Use visibility to hide element\r
7038  * @static\r
7039  * @type Number\r
7040  */\r
7041 Ext.Element.VISIBILITY = 1;\r
7042 /**\r
7043  * Visibility mode constant for use with {@link #setVisibilityMode}. Use display to hide element\r
7044  * @static\r
7045  * @type Number\r
7046  */\r
7047 Ext.Element.DISPLAY = 2;\r
7048 \r
7049 Ext.Element.addMethods(function(){\r
7050     var VISIBILITY = "visibility",\r
7051         DISPLAY = "display",\r
7052         HIDDEN = "hidden",\r
7053         NONE = "none",      \r
7054         ORIGINALDISPLAY = 'originalDisplay',\r
7055         VISMODE = 'visibilityMode',\r
7056         ELDISPLAY = Ext.Element.DISPLAY,\r
7057         data = Ext.Element.data,\r
7058         getDisplay = function(dom){\r
7059             var d = data(dom, ORIGINALDISPLAY);\r
7060             if(d === undefined){\r
7061                 data(dom, ORIGINALDISPLAY, d = '');\r
7062             }\r
7063             return d;\r
7064         },\r
7065         getVisMode = function(dom){\r
7066             var m = data(dom, VISMODE);\r
7067             if(m === undefined){\r
7068                 data(dom, VISMODE, m = 1)\r
7069             }\r
7070             return m;\r
7071         };\r
7072     \r
7073     return {\r
7074         /**\r
7075          * The element's default display mode  (defaults to "")\r
7076          * @type String\r
7077          */\r
7078         originalDisplay : "",\r
7079         visibilityMode : 1,\r
7080         \r
7081         /**\r
7082          * Sets the element's visibility mode. When setVisible() is called it\r
7083          * will use this to determine whether to set the visibility or the display property.\r
7084          * @param {Number} visMode Ext.Element.VISIBILITY or Ext.Element.DISPLAY\r
7085          * @return {Ext.Element} this\r
7086          */\r
7087         setVisibilityMode : function(visMode){  \r
7088             data(this.dom, VISMODE, visMode);\r
7089             return this;\r
7090         },\r
7091         \r
7092         /**\r
7093          * Perform custom animation on this element.\r
7094          * <div><ul class="mdetail-params">\r
7095          * <li><u>Animation Properties</u></li>\r
7096          * \r
7097          * <p>The Animation Control Object enables gradual transitions for any member of an\r
7098          * element's style object that takes a numeric value including but not limited to\r
7099          * these properties:</p><div><ul class="mdetail-params">\r
7100          * <li><tt>bottom, top, left, right</tt></li>\r
7101          * <li><tt>height, width</tt></li>\r
7102          * <li><tt>margin, padding</tt></li>\r
7103          * <li><tt>borderWidth</tt></li>\r
7104          * <li><tt>opacity</tt></li>\r
7105          * <li><tt>fontSize</tt></li>\r
7106          * <li><tt>lineHeight</tt></li>\r
7107          * </ul></div>\r
7108          * \r
7109          * \r
7110          * <li><u>Animation Property Attributes</u></li>\r
7111          * \r
7112          * <p>Each Animation Property is a config object with optional properties:</p>\r
7113          * <div><ul class="mdetail-params">\r
7114          * <li><tt>by</tt>*  : relative change - start at current value, change by this value</li>\r
7115          * <li><tt>from</tt> : ignore current value, start from this value</li>\r
7116          * <li><tt>to</tt>*  : start at current value, go to this value</li>\r
7117          * <li><tt>unit</tt> : any allowable unit specification</li>\r
7118          * <p>* do not specify both <tt>to</tt> and <tt>by</tt> for an animation property</p>\r
7119          * </ul></div>\r
7120          * \r
7121          * <li><u>Animation Types</u></li>\r
7122          * \r
7123          * <p>The supported animation types:</p><div><ul class="mdetail-params">\r
7124          * <li><tt>'run'</tt> : Default\r
7125          * <pre><code>\r
7126 var el = Ext.get('complexEl');\r
7127 el.animate(\r
7128     // animation control object\r
7129     {\r
7130         borderWidth: {to: 3, from: 0},\r
7131         opacity: {to: .3, from: 1},\r
7132         height: {to: 50, from: el.getHeight()},\r
7133         width: {to: 300, from: el.getWidth()},\r
7134         top  : {by: - 100, unit: 'px'},\r
7135     },\r
7136     0.35,      // animation duration\r
7137     null,      // callback\r
7138     'easeOut', // easing method\r
7139     'run'      // animation type ('run','color','motion','scroll')    \r
7140 );\r
7141          * </code></pre>\r
7142          * </li>\r
7143          * <li><tt>'color'</tt>\r
7144          * <p>Animates transition of background, text, or border colors.</p>\r
7145          * <pre><code>\r
7146 el.animate(\r
7147     // animation control object\r
7148     {\r
7149         color: { to: '#06e' },\r
7150         backgroundColor: { to: '#e06' }\r
7151     },\r
7152     0.35,      // animation duration\r
7153     null,      // callback\r
7154     'easeOut', // easing method\r
7155     'color'    // animation type ('run','color','motion','scroll')    \r
7156 );\r
7157          * </code></pre> \r
7158          * </li>\r
7159          * \r
7160          * <li><tt>'motion'</tt>\r
7161          * <p>Animates the motion of an element to/from specific points using optional bezier\r
7162          * way points during transit.</p>\r
7163          * <pre><code>\r
7164 el.animate(\r
7165     // animation control object\r
7166     {\r
7167         borderWidth: {to: 3, from: 0},\r
7168         opacity: {to: .3, from: 1},\r
7169         height: {to: 50, from: el.getHeight()},\r
7170         width: {to: 300, from: el.getWidth()},\r
7171         top  : {by: - 100, unit: 'px'},\r
7172         points: {\r
7173             to: [50, 100],  // go to this point\r
7174             control: [      // optional bezier way points\r
7175                 [ 600, 800],\r
7176                 [-100, 200]\r
7177             ]\r
7178         }\r
7179     },\r
7180     3000,      // animation duration (milliseconds!)\r
7181     null,      // callback\r
7182     'easeOut', // easing method\r
7183     'motion'   // animation type ('run','color','motion','scroll')    \r
7184 );\r
7185          * </code></pre> \r
7186          * </li>\r
7187          * <li><tt>'scroll'</tt>\r
7188          * <p>Animate horizontal or vertical scrolling of an overflowing page element.</p>\r
7189          * <pre><code>\r
7190 el.animate(\r
7191     // animation control object\r
7192     {\r
7193         scroll: {to: [400, 300]}\r
7194     },\r
7195     0.35,      // animation duration\r
7196     null,      // callback\r
7197     'easeOut', // easing method\r
7198     'scroll'   // animation type ('run','color','motion','scroll')    \r
7199 );\r
7200          * </code></pre> \r
7201          * </li>\r
7202          * </ul></div>\r
7203          * \r
7204          * </ul></div>\r
7205          * \r
7206          * @param {Object} args The animation control args\r
7207          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to <tt>.35</tt>)\r
7208          * @param {Function} onComplete (optional) Function to call when animation completes\r
7209          * @param {String} easing (optional) {@link Ext.Fx#easing} method to use (defaults to <tt>'easeOut'</tt>)\r
7210          * @param {String} animType (optional) <tt>'run'</tt> is the default. Can also be <tt>'color'</tt>,\r
7211          * <tt>'motion'</tt>, or <tt>'scroll'</tt>\r
7212          * @return {Ext.Element} this\r
7213          */\r
7214         animate : function(args, duration, onComplete, easing, animType){       \r
7215             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);\r
7216             return this;\r
7217         },\r
7218     \r
7219         /*\r
7220          * @private Internal animation call\r
7221          */\r
7222         anim : function(args, opt, animType, defaultDur, defaultEase, cb){\r
7223             animType = animType || 'run';\r
7224             opt = opt || {};\r
7225             var me = this,              \r
7226                 anim = Ext.lib.Anim[animType](\r
7227                     me.dom, \r
7228                     args,\r
7229                     (opt.duration || defaultDur) || .35,\r
7230                     (opt.easing || defaultEase) || 'easeOut',\r
7231                     function(){\r
7232                         if(cb) cb.call(me);\r
7233                         if(opt.callback) opt.callback.call(opt.scope || me, me, opt);\r
7234                     },\r
7235                     me\r
7236                 );\r
7237             opt.anim = anim;\r
7238             return anim;\r
7239         },\r
7240     \r
7241         // private legacy anim prep\r
7242         preanim : function(a, i){\r
7243             return !a[i] ? false : (Ext.isObject(a[i]) ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});\r
7244         },\r
7245         \r
7246         /**\r
7247          * Checks whether the element is currently visible using both visibility and display properties.         \r
7248          * @return {Boolean} True if the element is currently visible, else false\r
7249          */\r
7250         isVisible : function() {\r
7251             return !this.isStyle(VISIBILITY, HIDDEN) && !this.isStyle(DISPLAY, NONE);\r
7252         },\r
7253         \r
7254         /**\r
7255          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use\r
7256          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.\r
7257          * @param {Boolean} visible Whether the element is visible\r
7258          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object\r
7259          * @return {Ext.Element} this\r
7260          */\r
7261          setVisible : function(visible, animate){\r
7262             var me = this,\r
7263                 dom = me.dom,\r
7264                 isDisplay = getVisMode(this.dom) == ELDISPLAY;\r
7265                 \r
7266             if (!animate || !me.anim) {\r
7267                 if(isDisplay){\r
7268                     me.setDisplayed(visible);\r
7269                 }else{\r
7270                     me.fixDisplay();\r
7271                     dom.style.visibility = visible ? "visible" : HIDDEN;\r
7272                 }\r
7273             }else{\r
7274                 // closure for composites            \r
7275                 if(visible){\r
7276                     me.setOpacity(.01);\r
7277                     me.setVisible(true);\r
7278                 }\r
7279                 me.anim({opacity: { to: (visible?1:0) }},\r
7280                         me.preanim(arguments, 1),\r
7281                         null,\r
7282                         .35,\r
7283                         'easeIn',\r
7284                         function(){\r
7285                              if(!visible){\r
7286                                  dom.style[isDisplay ? DISPLAY : VISIBILITY] = (isDisplay) ? NONE : HIDDEN;                     \r
7287                                  Ext.fly(dom).setOpacity(1);\r
7288                              }\r
7289                         });\r
7290             }\r
7291             return me;\r
7292         },\r
7293     \r
7294         /**\r
7295          * Toggles the element's visibility or display, depending on visibility mode.\r
7296          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object\r
7297          * @return {Ext.Element} this\r
7298          */\r
7299         toggle : function(animate){\r
7300             var me = this;\r
7301             me.setVisible(!me.isVisible(), me.preanim(arguments, 0));\r
7302             return me;\r
7303         },\r
7304     \r
7305         /**\r
7306          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.\r
7307          * @param {Mixed} value Boolean value to display the element using its default display, or a string to set the display directly.\r
7308          * @return {Ext.Element} this\r
7309          */\r
7310         setDisplayed : function(value) {            \r
7311             if(typeof value == "boolean"){\r
7312                value = value ? getDisplay(this.dom) : NONE;\r
7313             }\r
7314             this.setStyle(DISPLAY, value);\r
7315             return this;\r
7316         },\r
7317         \r
7318         // private\r
7319         fixDisplay : function(){\r
7320             var me = this;\r
7321             if(me.isStyle(DISPLAY, NONE)){\r
7322                 me.setStyle(VISIBILITY, HIDDEN);\r
7323                 me.setStyle(DISPLAY, getDisplay(this.dom)); // first try reverting to default\r
7324                 if(me.isStyle(DISPLAY, NONE)){ // if that fails, default to block\r
7325                     me.setStyle(DISPLAY, "block");\r
7326                 }\r
7327             }\r
7328         },\r
7329     \r
7330         /**\r
7331          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.\r
7332          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object\r
7333          * @return {Ext.Element} this\r
7334          */\r
7335         hide : function(animate){\r
7336             this.setVisible(false, this.preanim(arguments, 0));\r
7337             return this;\r
7338         },\r
7339     \r
7340         /**\r
7341         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.\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         show : function(animate){\r
7346             this.setVisible(true, this.preanim(arguments, 0));\r
7347             return this;\r
7348         }\r
7349     }\r
7350 }());/**\r
7351  * @class Ext.Element\r
7352  */\r
7353 Ext.Element.addMethods(\r
7354 function(){\r
7355     var VISIBILITY = "visibility",\r
7356         DISPLAY = "display",\r
7357         HIDDEN = "hidden",\r
7358         NONE = "none",\r
7359             XMASKED = "x-masked",\r
7360                 XMASKEDRELATIVE = "x-masked-relative",\r
7361         data = Ext.Element.data;\r
7362                 \r
7363         return {\r
7364                 /**\r
7365              * Checks whether the element is currently visible using both visibility and display properties.\r
7366              * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)\r
7367              * @return {Boolean} True if the element is currently visible, else false\r
7368              */\r
7369             isVisible : function(deep) {\r
7370                 var vis = !this.isStyle(VISIBILITY,HIDDEN) && !this.isStyle(DISPLAY,NONE),\r
7371                         p = this.dom.parentNode;\r
7372                 if(deep !== true || !vis){\r
7373                     return vis;\r
7374                 }               \r
7375                 while(p && !/body/i.test(p.tagName)){\r
7376                     if(!Ext.fly(p, '_isVisible').isVisible()){\r
7377                         return false;\r
7378                     }\r
7379                     p = p.parentNode;\r
7380                 }\r
7381                 return true;\r
7382             },\r
7383             \r
7384             /**\r
7385              * Returns true if display is not "none"\r
7386              * @return {Boolean}\r
7387              */\r
7388             isDisplayed : function() {\r
7389                 return !this.isStyle(DISPLAY, NONE);\r
7390             },\r
7391             \r
7392                 /**\r
7393              * Convenience method for setVisibilityMode(Element.DISPLAY)\r
7394              * @param {String} display (optional) What to set display to when visible\r
7395              * @return {Ext.Element} this\r
7396              */\r
7397             enableDisplayMode : function(display){          \r
7398                 this.setVisibilityMode(Ext.Element.DISPLAY);\r
7399                 if(!Ext.isEmpty(display)){\r
7400                 data(this.dom, 'originalDisplay', display);\r
7401             }\r
7402                 return this;\r
7403             },\r
7404             \r
7405                 /**\r
7406              * Puts a mask over this element to disable user interaction. Requires core.css.\r
7407              * This method can only be applied to elements which accept child nodes.\r
7408              * @param {String} msg (optional) A message to display in the mask\r
7409              * @param {String} msgCls (optional) A css class to apply to the msg element\r
7410              * @return {Element} The mask element\r
7411              */\r
7412             mask : function(msg, msgCls){\r
7413                     var me = this,\r
7414                         dom = me.dom,\r
7415                         dh = Ext.DomHelper,\r
7416                         EXTELMASKMSG = "ext-el-mask-msg",\r
7417                 el, \r
7418                 mask;\r
7419                         \r
7420                 if(me.getStyle("position") == "static"){\r
7421                     me.addClass(XMASKEDRELATIVE);\r
7422                 }\r
7423                 if((el = data(dom, 'maskMsg'))){\r
7424                     el.remove();\r
7425                 }\r
7426                 if((el = data(dom, 'mask'))){\r
7427                     el.remove();\r
7428                 }\r
7429         \r
7430             mask = dh.append(dom, {cls : "ext-el-mask"}, true);\r
7431                 data(dom, 'mask', mask);\r
7432         \r
7433                 me.addClass(XMASKED);\r
7434                 mask.setDisplayed(true);\r
7435                 if(typeof msg == 'string'){\r
7436                 var mm = dh.append(dom, {cls : EXTELMASKMSG, cn:{tag:'div'}}, true);\r
7437                 data(dom, 'maskMsg', mm);\r
7438                     mm.dom.className = msgCls ? EXTELMASKMSG + " " + msgCls : EXTELMASKMSG;\r
7439                     mm.dom.firstChild.innerHTML = msg;\r
7440                     mm.setDisplayed(true);\r
7441                     mm.center(me);\r
7442                 }\r
7443                 if(Ext.isIE && !(Ext.isIE7 && Ext.isStrict) && me.getStyle('height') == 'auto'){ // ie will not expand full height automatically\r
7444                     mask.setSize(undefined, me.getHeight());\r
7445                 }\r
7446                 return mask;\r
7447             },\r
7448         \r
7449             /**\r
7450              * Removes a previously applied mask.\r
7451              */\r
7452             unmask : function(){\r
7453                     var me = this,\r
7454                 dom = me.dom,\r
7455                         mask = data(dom, 'mask'),\r
7456                         maskMsg = data(dom, 'maskMsg');\r
7457                 if(mask){\r
7458                     if(maskMsg){\r
7459                         maskMsg.remove();\r
7460                     data(dom, 'maskMsg', undefined);\r
7461                     }\r
7462                     mask.remove();\r
7463                 data(dom, 'mask', undefined);\r
7464                 }\r
7465                 me.removeClass([XMASKED, XMASKEDRELATIVE]);\r
7466             },\r
7467         \r
7468             /**\r
7469              * Returns true if this element is masked\r
7470              * @return {Boolean}\r
7471              */\r
7472             isMasked : function(){\r
7473             var m = data(this.dom, 'mask');\r
7474                 return m && m.isVisible();\r
7475             },\r
7476             \r
7477             /**\r
7478              * Creates an iframe shim for this element to keep selects and other windowed objects from\r
7479              * showing through.\r
7480              * @return {Ext.Element} The new shim element\r
7481              */\r
7482             createShim : function(){\r
7483                 var el = document.createElement('iframe'),              \r
7484                         shim;\r
7485                 el.frameBorder = '0';\r
7486                 el.className = 'ext-shim';\r
7487                 el.src = Ext.SSL_SECURE_URL;\r
7488                 shim = Ext.get(this.dom.parentNode.insertBefore(el, this.dom));\r
7489                 shim.autoBoxAdjust = false;\r
7490                 return shim;\r
7491             }\r
7492     };\r
7493 }());/**\r
7494  * @class Ext.Element\r
7495  */\r
7496 Ext.Element.addMethods({\r
7497     /**\r
7498      * Convenience method for constructing a KeyMap\r
7499      * @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
7500      * <code>{key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}</code>\r
7501      * @param {Function} fn The function to call\r
7502      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the specified function is executed. Defaults to this Element.\r
7503      * @return {Ext.KeyMap} The KeyMap created\r
7504      */\r
7505     addKeyListener : function(key, fn, scope){\r
7506         var config;\r
7507         if(!Ext.isObject(key) || Ext.isArray(key)){\r
7508             config = {\r
7509                 key: key,\r
7510                 fn: fn,\r
7511                 scope: scope\r
7512             };\r
7513         }else{\r
7514             config = {\r
7515                 key : key.key,\r
7516                 shift : key.shift,\r
7517                 ctrl : key.ctrl,\r
7518                 alt : key.alt,\r
7519                 fn: fn,\r
7520                 scope: scope\r
7521             };\r
7522         }\r
7523         return new Ext.KeyMap(this, config);\r
7524     },\r
7525 \r
7526     /**\r
7527      * Creates a KeyMap for this element\r
7528      * @param {Object} config The KeyMap config. See {@link Ext.KeyMap} for more details\r
7529      * @return {Ext.KeyMap} The KeyMap created\r
7530      */\r
7531     addKeyMap : function(config){\r
7532         return new Ext.KeyMap(this, config);\r
7533     }\r
7534 });(function(){\r
7535     // contants\r
7536     var NULL = null,\r
7537         UNDEFINED = undefined,\r
7538         TRUE = true,\r
7539         FALSE = false,\r
7540         SETX = "setX",\r
7541         SETY = "setY",\r
7542         SETXY = "setXY",\r
7543         LEFT = "left",\r
7544         BOTTOM = "bottom",\r
7545         TOP = "top",\r
7546         RIGHT = "right",\r
7547         HEIGHT = "height",\r
7548         WIDTH = "width",\r
7549         POINTS = "points",\r
7550         HIDDEN = "hidden",\r
7551         ABSOLUTE = "absolute",\r
7552         VISIBLE = "visible",\r
7553         MOTION = "motion",\r
7554         POSITION = "position",\r
7555         EASEOUT = "easeOut",\r
7556         /*\r
7557          * Use a light flyweight here since we are using so many callbacks and are always assured a DOM element\r
7558          */\r
7559         flyEl = new Ext.Element.Flyweight(),\r
7560         queues = {},\r
7561         getObject = function(o){\r
7562             return o || {};\r
7563         },\r
7564         fly = function(dom){\r
7565             flyEl.dom = dom;\r
7566             flyEl.id = Ext.id(dom);\r
7567             return flyEl;\r
7568         },\r
7569         /*\r
7570          * Queueing now stored outside of the element due to closure issues\r
7571          */\r
7572         getQueue = function(id){\r
7573             if(!queues[id]){\r
7574                 queues[id] = [];\r
7575             }\r
7576             return queues[id];\r
7577         },\r
7578         setQueue = function(id, value){\r
7579             queues[id] = value;\r
7580         };\r
7581         \r
7582 //Notifies Element that fx methods are available\r
7583 Ext.enableFx = TRUE;\r
7584 \r
7585 /**\r
7586  * @class Ext.Fx\r
7587  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied\r
7588  * to the {@link Ext.Element} interface when included, so all effects calls should be performed via {@link Ext.Element}.\r
7589  * Conversely, since the effects are not actually defined in {@link Ext.Element}, Ext.Fx <b>must</b> be\r
7590  * {@link Ext#enableFx included} in order for the Element effects to work.</p><br/>\r
7591  * \r
7592  * <p><b><u>Method Chaining</u></b></p>\r
7593  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that\r
7594  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single\r
7595  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.\r
7596  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,\r
7597  * 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
7598  * expected results and should be done with care.  Also see <tt>{@link #callback}</tt>.</p><br/>\r
7599  *\r
7600  * <p><b><u>Anchor Options for Motion Effects</u></b></p>\r
7601  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element\r
7602  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>\r
7603 <pre>\r
7604 Value  Description\r
7605 -----  -----------------------------\r
7606 tl     The top left corner\r
7607 t      The center of the top edge\r
7608 tr     The top right corner\r
7609 l      The center of the left edge\r
7610 r      The center of the right edge\r
7611 bl     The bottom left corner\r
7612 b      The center of the bottom edge\r
7613 br     The bottom right corner\r
7614 </pre>\r
7615  * <b>Note</b>: some Fx methods accept specific custom config parameters.  The options shown in the Config Options\r
7616  * section below are common options that can be passed to any Fx method unless otherwise noted.</b>\r
7617  * \r
7618  * @cfg {Function} callback A function called when the effect is finished.  Note that effects are queued internally by the\r
7619  * Fx class, so a callback is not required to specify another effect -- effects can simply be chained together\r
7620  * and called in sequence (see note for <b><u>Method Chaining</u></b> above), for example:<pre><code>\r
7621  * el.slideIn().highlight();\r
7622  * </code></pre>\r
7623  * The callback is intended for any additional code that should run once a particular effect has completed. The Element\r
7624  * being operated upon is passed as the first parameter.\r
7625  * \r
7626  * @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
7627  * \r
7628  * @cfg {String} easing A valid Ext.lib.Easing value for the effect:</p><div class="mdetail-params"><ul>\r
7629  * <li><b><tt>backBoth</tt></b></li>\r
7630  * <li><b><tt>backIn</tt></b></li>\r
7631  * <li><b><tt>backOut</tt></b></li>\r
7632  * <li><b><tt>bounceBoth</tt></b></li>\r
7633  * <li><b><tt>bounceIn</tt></b></li>\r
7634  * <li><b><tt>bounceOut</tt></b></li>\r
7635  * <li><b><tt>easeBoth</tt></b></li>\r
7636  * <li><b><tt>easeBothStrong</tt></b></li>\r
7637  * <li><b><tt>easeIn</tt></b></li>\r
7638  * <li><b><tt>easeInStrong</tt></b></li>\r
7639  * <li><b><tt>easeNone</tt></b></li>\r
7640  * <li><b><tt>easeOut</tt></b></li>\r
7641  * <li><b><tt>easeOutStrong</tt></b></li>\r
7642  * <li><b><tt>elasticBoth</tt></b></li>\r
7643  * <li><b><tt>elasticIn</tt></b></li>\r
7644  * <li><b><tt>elasticOut</tt></b></li>\r
7645  * </ul></div>\r
7646  *\r
7647  * @cfg {String} afterCls A css class to apply after the effect\r
7648  * @cfg {Number} duration The length of time (in seconds) that the effect should last\r
7649  * \r
7650  * @cfg {Number} endOpacity Only applicable for {@link #fadeIn} or {@link #fadeOut}, a number between\r
7651  * <tt>0</tt> and <tt>1</tt> inclusive to configure the ending opacity value.\r
7652  *  \r
7653  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes\r
7654  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to \r
7655  * effects that end with the element being visually hidden, ignored otherwise)\r
7656  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. <tt>"width:100px"</tt>, or an object\r
7657  * in the form <tt>{width:"100px"}</tt>, or a function which returns such a specification that will be applied to the\r
7658  * Element after the effect finishes.\r
7659  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs\r
7660  * @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
7661  * @cfg {Boolean} stopFx Whether preceding effects should be stopped and removed before running current effect (only applies to non blocking effects)\r
7662  */\r
7663 Ext.Fx = {\r
7664     \r
7665     // private - calls the function taking arguments from the argHash based on the key.  Returns the return value of the function.\r
7666     //           this is useful for replacing switch statements (for example).\r
7667     switchStatements : function(key, fn, argHash){\r
7668         return fn.apply(this, argHash[key]);\r
7669     },\r
7670     \r
7671     /**\r
7672      * Slides the element into view.  An anchor point can be optionally passed to set the point of\r
7673      * origin for the slide effect.  This function automatically handles wrapping the element with\r
7674      * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.\r
7675      * Usage:\r
7676      *<pre><code>\r
7677 // default: slide the element in from the top\r
7678 el.slideIn();\r
7679 \r
7680 // custom: slide the element in from the right with a 2-second duration\r
7681 el.slideIn('r', { duration: 2 });\r
7682 \r
7683 // common config options shown with default values\r
7684 el.slideIn('t', {\r
7685     easing: 'easeOut',\r
7686     duration: .5\r
7687 });\r
7688 </code></pre>\r
7689      * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')\r
7690      * @param {Object} options (optional) Object literal with any of the Fx config options\r
7691      * @return {Ext.Element} The Element\r
7692      */\r
7693     slideIn : function(anchor, o){ \r
7694         o = getObject(o);\r
7695         var me = this,\r
7696             dom = me.dom,\r
7697             st = dom.style,\r
7698             xy,\r
7699             r,\r
7700             b,              \r
7701             wrap,               \r
7702             after,\r
7703             st,\r
7704             args, \r
7705             pt,\r
7706             bw,\r
7707             bh;\r
7708             \r
7709         anchor = anchor || "t";\r
7710 \r
7711         me.queueFx(o, function(){            \r
7712             xy = fly(dom).getXY();\r
7713             // fix display to visibility\r
7714             fly(dom).fixDisplay();            \r
7715             \r
7716             // restore values after effect\r
7717             r = fly(dom).getFxRestore();      \r
7718             b = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: dom.offsetWidth, height: dom.offsetHeight};\r
7719             b.right = b.x + b.width;\r
7720             b.bottom = b.y + b.height;\r
7721             \r
7722             // fixed size for slide\r
7723             fly(dom).setWidth(b.width).setHeight(b.height);            \r
7724             \r
7725             // wrap if needed\r
7726             wrap = fly(dom).fxWrap(r.pos, o, HIDDEN);\r
7727             \r
7728             st.visibility = VISIBLE;\r
7729             st.position = ABSOLUTE;\r
7730             \r
7731             // clear out temp styles after slide and unwrap\r
7732             function after(){\r
7733                  fly(dom).fxUnwrap(wrap, r.pos, o);\r
7734                  st.width = r.width;\r
7735                  st.height = r.height;\r
7736                  fly(dom).afterFx(o);\r
7737             }\r
7738             \r
7739             // time to calculate the positions        \r
7740             pt = {to: [b.x, b.y]}; \r
7741             bw = {to: b.width};\r
7742             bh = {to: b.height};\r
7743                 \r
7744             function argCalc(wrap, style, ww, wh, sXY, sXYval, s1, s2, w, h, p){                    \r
7745                 var ret = {};\r
7746                 fly(wrap).setWidth(ww).setHeight(wh);\r
7747                 if(fly(wrap)[sXY]){\r
7748                     fly(wrap)[sXY](sXYval);                  \r
7749                 }\r
7750                 style[s1] = style[s2] = "0";                    \r
7751                 if(w){\r
7752                     ret.width = w\r
7753                 };\r
7754                 if(h){\r
7755                     ret.height = h;\r
7756                 }\r
7757                 if(p){\r
7758                     ret.points = p;\r
7759                 }\r
7760                 return ret;\r
7761             };\r
7762 \r
7763             args = fly(dom).switchStatements(anchor.toLowerCase(), argCalc, {\r
7764                     t  : [wrap, st, b.width, 0, NULL, NULL, LEFT, BOTTOM, NULL, bh, NULL],\r
7765                     l  : [wrap, st, 0, b.height, NULL, NULL, RIGHT, TOP, bw, NULL, NULL],\r
7766                     r  : [wrap, st, b.width, b.height, SETX, b.right, LEFT, TOP, NULL, NULL, pt],\r
7767                     b  : [wrap, st, b.width, b.height, SETY, b.bottom, LEFT, TOP, NULL, bh, pt],\r
7768                     tl : [wrap, st, 0, 0, NULL, NULL, RIGHT, BOTTOM, bw, bh, pt],\r
7769                     bl : [wrap, st, 0, 0, SETY, b.y + b.height, RIGHT, TOP, bw, bh, pt],\r
7770                     br : [wrap, st, 0, 0, SETXY, [b.right, b.bottom], LEFT, TOP, bw, bh, pt],\r
7771                     tr : [wrap, st, 0, 0, SETX, b.x + b.width, LEFT, BOTTOM, bw, bh, pt]\r
7772                 });\r
7773             \r
7774             st.visibility = VISIBLE;\r
7775             fly(wrap).show();\r
7776 \r
7777             arguments.callee.anim = fly(wrap).fxanim(args,\r
7778                 o,\r
7779                 MOTION,\r
7780                 .5,\r
7781                 EASEOUT, \r
7782                 after);\r
7783         });\r
7784         return me;\r
7785     },\r
7786     \r
7787     /**\r
7788      * Slides the element out of view.  An anchor point can be optionally passed to set the end point\r
7789      * for the slide effect.  When the effect is completed, the element will be hidden (visibility = \r
7790      * 'hidden') but block elements will still take up space in the document.  The element must be removed\r
7791      * from the DOM using the 'remove' config option if desired.  This function automatically handles \r
7792      * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.\r
7793      * Usage:\r
7794      *<pre><code>\r
7795 // default: slide the element out to the top\r
7796 el.slideOut();\r
7797 \r
7798 // custom: slide the element out to the right with a 2-second duration\r
7799 el.slideOut('r', { duration: 2 });\r
7800 \r
7801 // common config options shown with default values\r
7802 el.slideOut('t', {\r
7803     easing: 'easeOut',\r
7804     duration: .5,\r
7805     remove: false,\r
7806     useDisplay: false\r
7807 });\r
7808 </code></pre>\r
7809      * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')\r
7810      * @param {Object} options (optional) Object literal with any of the Fx config options\r
7811      * @return {Ext.Element} The Element\r
7812      */\r
7813     slideOut : function(anchor, o){\r
7814         o = getObject(o);\r
7815         var me = this,\r
7816             dom = me.dom,\r
7817             st = dom.style,\r
7818             xy = me.getXY(),\r
7819             wrap,\r
7820             r,\r
7821             b,\r
7822             a,\r
7823             zero = {to: 0}; \r
7824                     \r
7825         anchor = anchor || "t";\r
7826 \r
7827         me.queueFx(o, function(){\r
7828             \r
7829             // restore values after effect\r
7830             r = fly(dom).getFxRestore(); \r
7831             b = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: dom.offsetWidth, height: dom.offsetHeight};\r
7832             b.right = b.x + b.width;\r
7833             b.bottom = b.y + b.height;\r
7834                 \r
7835             // fixed size for slide   \r
7836             fly(dom).setWidth(b.width).setHeight(b.height);\r
7837 \r
7838             // wrap if needed\r
7839             wrap = fly(dom).fxWrap(r.pos, o, VISIBLE);\r
7840                 \r
7841             st.visibility = VISIBLE;\r
7842             st.position = ABSOLUTE;\r
7843             fly(wrap).setWidth(b.width).setHeight(b.height);            \r
7844 \r
7845             function after(){\r
7846                 o.useDisplay ? fly(dom).setDisplayed(FALSE) : fly(dom).hide();                \r
7847                 fly(dom).fxUnwrap(wrap, r.pos, o);\r
7848                 st.width = r.width;\r
7849                 st.height = r.height;\r
7850                 fly(dom).afterFx(o);\r
7851             }            \r
7852             \r
7853             function argCalc(style, s1, s2, p1, v1, p2, v2, p3, v3){                    \r
7854                 var ret = {};\r
7855                 \r
7856                 style[s1] = style[s2] = "0";\r
7857                 ret[p1] = v1;               \r
7858                 if(p2){\r
7859                     ret[p2] = v2;               \r
7860                 }\r
7861                 if(p3){\r
7862                     ret[p3] = v3;\r
7863                 }\r
7864                 \r
7865                 return ret;\r
7866             };\r
7867             \r
7868             a = fly(dom).switchStatements(anchor.toLowerCase(), argCalc, {\r
7869                 t  : [st, LEFT, BOTTOM, HEIGHT, zero],\r
7870                 l  : [st, RIGHT, TOP, WIDTH, zero],\r
7871                 r  : [st, LEFT, TOP, WIDTH, zero, POINTS, {to : [b.right, b.y]}],\r
7872                 b  : [st, LEFT, TOP, HEIGHT, zero, POINTS, {to : [b.x, b.bottom]}],\r
7873                 tl : [st, RIGHT, BOTTOM, WIDTH, zero, HEIGHT, zero],\r
7874                 bl : [st, RIGHT, TOP, WIDTH, zero, HEIGHT, zero, POINTS, {to : [b.x, b.bottom]}],\r
7875                 br : [st, LEFT, TOP, WIDTH, zero, HEIGHT, zero, POINTS, {to : [b.x + b.width, b.bottom]}],\r
7876                 tr : [st, LEFT, BOTTOM, WIDTH, zero, HEIGHT, zero, POINTS, {to : [b.right, b.y]}]\r
7877             });\r
7878             \r
7879             arguments.callee.anim = fly(wrap).fxanim(a,\r
7880                 o,\r
7881                 MOTION,\r
7882                 .5,\r
7883                 EASEOUT, \r
7884                 after);\r
7885         });\r
7886         return me;\r
7887     },\r
7888 \r
7889     /**\r
7890      * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the \r
7891      * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. \r
7892      * The element must be removed from the DOM using the 'remove' config option if desired.\r
7893      * Usage:\r
7894      *<pre><code>\r
7895 // default\r
7896 el.puff();\r
7897 \r
7898 // common config options shown with default values\r
7899 el.puff({\r
7900     easing: 'easeOut',\r
7901     duration: .5,\r
7902     remove: false,\r
7903     useDisplay: false\r
7904 });\r
7905 </code></pre>\r
7906      * @param {Object} options (optional) Object literal with any of the Fx config options\r
7907      * @return {Ext.Element} The Element\r
7908      */\r
7909     puff : function(o){\r
7910         o = getObject(o);\r
7911         var me = this,\r
7912             dom = me.dom,\r
7913             st = dom.style,\r
7914             width,\r
7915             height,\r
7916             r;\r
7917 \r
7918         me.queueFx(o, function(){\r
7919             width = fly(dom).getWidth();\r
7920             height = fly(dom).getHeight();\r
7921             fly(dom).clearOpacity();\r
7922             fly(dom).show();\r
7923 \r
7924             // restore values after effect\r
7925             r = fly(dom).getFxRestore();                   \r
7926             \r
7927             function after(){\r
7928                 o.useDisplay ? fly(dom).setDisplayed(FALSE) : fly(dom).hide();                  \r
7929                 fly(dom).clearOpacity();  \r
7930                 fly(dom).setPositioning(r.pos);\r
7931                 st.width = r.width;\r
7932                 st.height = r.height;\r
7933                 st.fontSize = '';\r
7934                 fly(dom).afterFx(o);\r
7935             }   \r
7936 \r
7937             arguments.callee.anim = fly(dom).fxanim({\r
7938                     width : {to : fly(dom).adjustWidth(width * 2)},\r
7939                     height : {to : fly(dom).adjustHeight(height * 2)},\r
7940                     points : {by : [-width * .5, -height * .5]},\r
7941                     opacity : {to : 0},\r
7942                     fontSize: {to : 200, unit: "%"}\r
7943                 },\r
7944                 o,\r
7945                 MOTION,\r
7946                 .5,\r
7947                 EASEOUT,\r
7948                  after);\r
7949         });\r
7950         return me;\r
7951     },\r
7952 \r
7953     /**\r
7954      * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).\r
7955      * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still \r
7956      * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.\r
7957      * Usage:\r
7958      *<pre><code>\r
7959 // default\r
7960 el.switchOff();\r
7961 \r
7962 // all config options shown with default values\r
7963 el.switchOff({\r
7964     easing: 'easeIn',\r
7965     duration: .3,\r
7966     remove: false,\r
7967     useDisplay: false\r
7968 });\r
7969 </code></pre>\r
7970      * @param {Object} options (optional) Object literal with any of the Fx config options\r
7971      * @return {Ext.Element} The Element\r
7972      */\r
7973     switchOff : function(o){\r
7974         o = getObject(o);\r
7975         var me = this,\r
7976             dom = me.dom,\r
7977             st = dom.style,\r
7978             r;\r
7979 \r
7980         me.queueFx(o, function(){\r
7981             fly(dom).clearOpacity();\r
7982             fly(dom).clip();\r
7983 \r
7984             // restore values after effect\r
7985             r = fly(dom).getFxRestore();\r
7986                 \r
7987             function after(){\r
7988                 o.useDisplay ? fly(dom).setDisplayed(FALSE) : fly(dom).hide();  \r
7989                 fly(dom).clearOpacity();\r
7990                 fly(dom).setPositioning(r.pos);\r
7991                 st.width = r.width;\r
7992                 st.height = r.height;   \r
7993                 fly(dom).afterFx(o);\r
7994             };\r
7995 \r
7996             fly(dom).fxanim({opacity : {to : 0.3}}, \r
7997                 NULL, \r
7998                 NULL, \r
7999                 .1, \r
8000                 NULL, \r
8001                 function(){                                 \r
8002                     fly(dom).clearOpacity();\r
8003                         (function(){                            \r
8004                             fly(dom).fxanim({\r
8005                                 height : {to : 1},\r
8006                                 points : {by : [0, fly(dom).getHeight() * .5]}\r
8007                             }, \r
8008                             o, \r
8009                             MOTION, \r
8010                             0.3, \r
8011                             'easeIn', \r
8012                             after);\r
8013                         }).defer(100);\r
8014                 });\r
8015         });\r
8016         return me;\r
8017     },\r
8018 \r
8019     /**\r
8020      * Highlights the Element by setting a color (applies to the background-color by default, but can be\r
8021      * changed using the "attr" config option) and then fading back to the original color. If no original\r
8022      * color is available, you should provide the "endColor" config option which will be cleared after the animation.\r
8023      * Usage:\r
8024 <pre><code>\r
8025 // default: highlight background to yellow\r
8026 el.highlight();\r
8027 \r
8028 // custom: highlight foreground text to blue for 2 seconds\r
8029 el.highlight("0000ff", { attr: 'color', duration: 2 });\r
8030 \r
8031 // common config options shown with default values\r
8032 el.highlight("ffff9c", {\r
8033     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value\r
8034     endColor: (current color) or "ffffff",\r
8035     easing: 'easeIn',\r
8036     duration: 1\r
8037 });\r
8038 </code></pre>\r
8039      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')\r
8040      * @param {Object} options (optional) Object literal with any of the Fx config options\r
8041      * @return {Ext.Element} The Element\r
8042      */ \r
8043     highlight : function(color, o){\r
8044         o = getObject(o);\r
8045         var me = this,\r
8046             dom = me.dom,\r
8047             attr = o.attr || "backgroundColor",\r
8048             a = {},\r
8049             restore;\r
8050 \r
8051         me.queueFx(o, function(){\r
8052             fly(dom).clearOpacity();\r
8053             fly(dom).show();\r
8054 \r
8055             function after(){\r
8056                 dom.style[attr] = restore;\r
8057                 fly(dom).afterFx(o);\r
8058             }            \r
8059             restore = dom.style[attr];\r
8060             a[attr] = {from: color || "ffff9c", to: o.endColor || fly(dom).getColor(attr) || "ffffff"};\r
8061             arguments.callee.anim = fly(dom).fxanim(a,\r
8062                 o,\r
8063                 'color',\r
8064                 1,\r
8065                 'easeIn', \r
8066                 after);\r
8067         });\r
8068         return me;\r
8069     },\r
8070 \r
8071    /**\r
8072     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.\r
8073     * Usage:\r
8074 <pre><code>\r
8075 // default: a single light blue ripple\r
8076 el.frame();\r
8077 \r
8078 // custom: 3 red ripples lasting 3 seconds total\r
8079 el.frame("ff0000", 3, { duration: 3 });\r
8080 \r
8081 // common config options shown with default values\r
8082 el.frame("C3DAF9", 1, {\r
8083     duration: 1 //duration of each individual ripple.\r
8084     // Note: Easing is not configurable and will be ignored if included\r
8085 });\r
8086 </code></pre>\r
8087     * @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
8088     * @param {Number} count (optional) The number of ripples to display (defaults to 1)\r
8089     * @param {Object} options (optional) Object literal with any of the Fx config options\r
8090     * @return {Ext.Element} The Element\r
8091     */\r
8092     frame : function(color, count, o){\r
8093         o = getObject(o);\r
8094         var me = this,\r
8095             dom = me.dom,\r
8096             proxy,\r
8097             active;\r
8098 \r
8099         me.queueFx(o, function(){\r
8100             color = color || '#C3DAF9';\r
8101             if(color.length == 6){\r
8102                 color = '#' + color;\r
8103             }            \r
8104             count = count || 1;\r
8105             fly(dom).show();\r
8106 \r
8107             var xy = fly(dom).getXY(),\r
8108                 b = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: dom.offsetWidth, height: dom.offsetHeight},\r
8109                 queue = function(){\r
8110                     proxy = fly(document.body || document.documentElement).createChild({\r
8111                         style:{\r
8112                             position : ABSOLUTE,\r
8113                             'z-index': 35000, // yee haw\r
8114                             border : '0px solid ' + color\r
8115                         }\r
8116                     });\r
8117                     return proxy.queueFx({}, animFn);\r
8118                 };\r
8119             \r
8120             \r
8121             arguments.callee.anim = {\r
8122                 isAnimated: true,\r
8123                 stop: function() {\r
8124                     count = 0;\r
8125                     proxy.stopFx();\r
8126                 }\r
8127             };\r
8128             \r
8129             function animFn(){\r
8130                 var scale = Ext.isBorderBox ? 2 : 1;\r
8131                 active = proxy.anim({\r
8132                     top : {from : b.y, to : b.y - 20},\r
8133                     left : {from : b.x, to : b.x - 20},\r
8134                     borderWidth : {from : 0, to : 10},\r
8135                     opacity : {from : 1, to : 0},\r
8136                     height : {from : b.height, to : b.height + 20 * scale},\r
8137                     width : {from : b.width, to : b.width + 20 * scale}\r
8138                 },{\r
8139                     duration: o.duration || 1,\r
8140                     callback: function() {\r
8141                         proxy.remove();\r
8142                         --count > 0 ? queue() : fly(dom).afterFx(o);\r
8143                     }\r
8144                 });\r
8145                 arguments.callee.anim = {\r
8146                     isAnimated: true,\r
8147                     stop: function(){\r
8148                         active.stop();\r
8149                     }\r
8150                 };\r
8151             };\r
8152             queue();\r
8153         });\r
8154         return me;\r
8155     },\r
8156 \r
8157    /**\r
8158     * Creates a pause before any subsequent queued effects begin.  If there are\r
8159     * no effects queued after the pause it will have no effect.\r
8160     * Usage:\r
8161 <pre><code>\r
8162 el.pause(1);\r
8163 </code></pre>\r
8164     * @param {Number} seconds The length of time to pause (in seconds)\r
8165     * @return {Ext.Element} The Element\r
8166     */\r
8167     pause : function(seconds){        \r
8168         var dom = this.dom,\r
8169             t;\r
8170 \r
8171         this.queueFx({}, function(){\r
8172             t = setTimeout(function(){\r
8173                 fly(dom).afterFx({});\r
8174             }, seconds * 1000);\r
8175             arguments.callee.anim = {\r
8176                 isAnimated: true,\r
8177                 stop: function(){\r
8178                     clearTimeout(t);\r
8179                     fly(dom).afterFx({});\r
8180                 }\r
8181             };\r
8182         });\r
8183         return this;\r
8184     },\r
8185 \r
8186    /**\r
8187     * Fade an element in (from transparent to opaque).  The ending opacity can be specified\r
8188     * using the <tt>{@link #endOpacity}</tt> config option.\r
8189     * Usage:\r
8190 <pre><code>\r
8191 // default: fade in from opacity 0 to 100%\r
8192 el.fadeIn();\r
8193 \r
8194 // custom: fade in from opacity 0 to 75% over 2 seconds\r
8195 el.fadeIn({ endOpacity: .75, duration: 2});\r
8196 \r
8197 // common config options shown with default values\r
8198 el.fadeIn({\r
8199     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)\r
8200     easing: 'easeOut',\r
8201     duration: .5\r
8202 });\r
8203 </code></pre>\r
8204     * @param {Object} options (optional) Object literal with any of the Fx config options\r
8205     * @return {Ext.Element} The Element\r
8206     */\r
8207     fadeIn : function(o){\r
8208         o = getObject(o);\r
8209         var me = this,\r
8210             dom = me.dom,\r
8211             to = o.endOpacity || 1;\r
8212         \r
8213         me.queueFx(o, function(){\r
8214             fly(dom).setOpacity(0);\r
8215             fly(dom).fixDisplay();\r
8216             dom.style.visibility = VISIBLE;\r
8217             arguments.callee.anim = fly(dom).fxanim({opacity:{to:to}},\r
8218                 o, NULL, .5, EASEOUT, function(){\r
8219                 if(to == 1){\r
8220                     fly(dom).clearOpacity();\r
8221                 }\r
8222                 fly(dom).afterFx(o);\r
8223             });\r
8224         });\r
8225         return me;\r
8226     },\r
8227 \r
8228    /**\r
8229     * Fade an element out (from opaque to transparent).  The ending opacity can be specified\r
8230     * using the <tt>{@link #endOpacity}</tt> config option.  Note that IE may require\r
8231     * <tt>{@link #useDisplay}:true</tt> in order to redisplay correctly.\r
8232     * Usage:\r
8233 <pre><code>\r
8234 // default: fade out from the element's current opacity to 0\r
8235 el.fadeOut();\r
8236 \r
8237 // custom: fade out from the element's current opacity to 25% over 2 seconds\r
8238 el.fadeOut({ endOpacity: .25, duration: 2});\r
8239 \r
8240 // common config options shown with default values\r
8241 el.fadeOut({\r
8242     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)\r
8243     easing: 'easeOut',\r
8244     duration: .5,\r
8245     remove: false,\r
8246     useDisplay: false\r
8247 });\r
8248 </code></pre>\r
8249     * @param {Object} options (optional) Object literal with any of the Fx config options\r
8250     * @return {Ext.Element} The Element\r
8251     */\r
8252     fadeOut : function(o){\r
8253         o = getObject(o);\r
8254         var me = this,\r
8255             dom = me.dom,\r
8256             style = dom.style,\r
8257             to = o.endOpacity || 0;         \r
8258         \r
8259         me.queueFx(o, function(){  \r
8260             arguments.callee.anim = fly(dom).fxanim({ \r
8261                 opacity : {to : to}},\r
8262                 o, \r
8263                 NULL, \r
8264                 .5, \r
8265                 EASEOUT, \r
8266                 function(){\r
8267                     if(to == 0){\r
8268                         Ext.Element.data(dom, 'visibilityMode') == Ext.Element.DISPLAY || o.useDisplay ? \r
8269                             style.display = "none" :\r
8270                             style.visibility = HIDDEN;\r
8271                             \r
8272                         fly(dom).clearOpacity();\r
8273                     }\r
8274                     fly(dom).afterFx(o);\r
8275             });\r
8276         });\r
8277         return me;\r
8278     },\r
8279 \r
8280    /**\r
8281     * Animates the transition of an element's dimensions from a starting height/width\r
8282     * to an ending height/width.  This method is a convenience implementation of {@link shift}.\r
8283     * Usage:\r
8284 <pre><code>\r
8285 // change height and width to 100x100 pixels\r
8286 el.scale(100, 100);\r
8287 \r
8288 // common config options shown with default values.  The height and width will default to\r
8289 // the element&#39;s existing values if passed as null.\r
8290 el.scale(\r
8291     [element&#39;s width],\r
8292     [element&#39;s height], {\r
8293         easing: 'easeOut',\r
8294         duration: .35\r
8295     }\r
8296 );\r
8297 </code></pre>\r
8298     * @param {Number} width  The new width (pass undefined to keep the original width)\r
8299     * @param {Number} height  The new height (pass undefined to keep the original height)\r
8300     * @param {Object} options (optional) Object literal with any of the Fx config options\r
8301     * @return {Ext.Element} The Element\r
8302     */\r
8303     scale : function(w, h, o){\r
8304         this.shift(Ext.apply({}, o, {\r
8305             width: w,\r
8306             height: h\r
8307         }));\r
8308         return this;\r
8309     },\r
8310 \r
8311    /**\r
8312     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.\r
8313     * Any of these properties not specified in the config object will not be changed.  This effect \r
8314     * requires that at least one new dimension, position or opacity setting must be passed in on\r
8315     * the config object in order for the function to have any effect.\r
8316     * Usage:\r
8317 <pre><code>\r
8318 // slide the element horizontally to x position 200 while changing the height and opacity\r
8319 el.shift({ x: 200, height: 50, opacity: .8 });\r
8320 \r
8321 // common config options shown with default values.\r
8322 el.shift({\r
8323     width: [element&#39;s width],\r
8324     height: [element&#39;s height],\r
8325     x: [element&#39;s x position],\r
8326     y: [element&#39;s y position],\r
8327     opacity: [element&#39;s opacity],\r
8328     easing: 'easeOut',\r
8329     duration: .35\r
8330 });\r
8331 </code></pre>\r
8332     * @param {Object} options  Object literal with any of the Fx config options\r
8333     * @return {Ext.Element} The Element\r
8334     */\r
8335     shift : function(o){\r
8336         o = getObject(o);\r
8337         var dom = this.dom,\r
8338             a = {};\r
8339                 \r
8340         this.queueFx(o, function(){\r
8341             for (var prop in o) {\r
8342                 if (o[prop] != UNDEFINED) {                                                 \r
8343                     a[prop] = {to : o[prop]};                   \r
8344                 }\r
8345             } \r
8346             \r
8347             a.width ? a.width.to = fly(dom).adjustWidth(o.width) : a;\r
8348             a.height ? a.height.to = fly(dom).adjustWidth(o.height) : a;   \r
8349             \r
8350             if (a.x || a.y || a.xy) {\r
8351                 a.points = a.xy || \r
8352                            {to : [ a.x ? a.x.to : fly(dom).getX(),\r
8353                                    a.y ? a.y.to : fly(dom).getY()]};                  \r
8354             }\r
8355 \r
8356             arguments.callee.anim = fly(dom).fxanim(a,\r
8357                 o, \r
8358                 MOTION, \r
8359                 .35, \r
8360                 EASEOUT, \r
8361                 function(){\r
8362                     fly(dom).afterFx(o);\r
8363                 });\r
8364         });\r
8365         return this;\r
8366     },\r
8367 \r
8368     /**\r
8369      * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the \r
8370      * ending point of the effect.\r
8371      * Usage:\r
8372      *<pre><code>\r
8373 // default: slide the element downward while fading out\r
8374 el.ghost();\r
8375 \r
8376 // custom: slide the element out to the right with a 2-second duration\r
8377 el.ghost('r', { duration: 2 });\r
8378 \r
8379 // common config options shown with default values\r
8380 el.ghost('b', {\r
8381     easing: 'easeOut',\r
8382     duration: .5,\r
8383     remove: false,\r
8384     useDisplay: false\r
8385 });\r
8386 </code></pre>\r
8387      * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')\r
8388      * @param {Object} options (optional) Object literal with any of the Fx config options\r
8389      * @return {Ext.Element} The Element\r
8390      */\r
8391     ghost : function(anchor, o){\r
8392         o = getObject(o);\r
8393         var me = this,\r
8394             dom = me.dom,\r
8395             st = dom.style,\r
8396             a = {opacity: {to: 0}, points: {}},\r
8397             pt = a.points,\r
8398             r,\r
8399             w,\r
8400             h;\r
8401             \r
8402         anchor = anchor || "b";\r
8403 \r
8404         me.queueFx(o, function(){\r
8405             // restore values after effect\r
8406             r = fly(dom).getFxRestore();\r
8407             w = fly(dom).getWidth();\r
8408             h = fly(dom).getHeight();\r
8409             \r
8410             function after(){\r
8411                 o.useDisplay ? fly(dom).setDisplayed(FALSE) : fly(dom).hide();   \r
8412                 fly(dom).clearOpacity();\r
8413                 fly(dom).setPositioning(r.pos);\r
8414                 st.width = r.width;\r
8415                 st.height = r.height;\r
8416                 fly(dom).afterFx(o);\r
8417             }\r
8418                 \r
8419             pt.by = fly(dom).switchStatements(anchor.toLowerCase(), function(v1,v2){ return [v1, v2];}, {\r
8420                t  : [0, -h],\r
8421                l  : [-w, 0],\r
8422                r  : [w, 0],\r
8423                b  : [0, h],\r
8424                tl : [-w, -h],\r
8425                bl : [-w, h],\r
8426                br : [w, h],\r
8427                tr : [w, -h] \r
8428             });\r
8429                 \r
8430             arguments.callee.anim = fly(dom).fxanim(a,\r
8431                 o,\r
8432                 MOTION,\r
8433                 .5,\r
8434                 EASEOUT, after);\r
8435         });\r
8436         return me;\r
8437     },\r
8438 \r
8439     /**\r
8440      * Ensures that all effects queued after syncFx is called on the element are\r
8441      * run concurrently.  This is the opposite of {@link #sequenceFx}.\r
8442      * @return {Ext.Element} The Element\r
8443      */\r
8444     syncFx : function(){\r
8445         var me = this;\r
8446         me.fxDefaults = Ext.apply(me.fxDefaults || {}, {\r
8447             block : FALSE,\r
8448             concurrent : TRUE,\r
8449             stopFx : FALSE\r
8450         });\r
8451         return me;\r
8452     },\r
8453 \r
8454     /**\r
8455      * Ensures that all effects queued after sequenceFx is called on the element are\r
8456      * run in sequence.  This is the opposite of {@link #syncFx}.\r
8457      * @return {Ext.Element} The Element\r
8458      */\r
8459     sequenceFx : function(){\r
8460         var me = this;\r
8461         me.fxDefaults = Ext.apply(me.fxDefaults || {}, {\r
8462             block : FALSE,\r
8463             concurrent : FALSE,\r
8464             stopFx : FALSE\r
8465         });\r
8466         return me;\r
8467     },\r
8468 \r
8469     /* @private */\r
8470     nextFx : function(){        \r
8471         var ef = getQueue(this.dom.id)[0];\r
8472         if(ef){\r
8473             ef.call(this);\r
8474         }\r
8475     },\r
8476 \r
8477     /**\r
8478      * Returns true if the element has any effects actively running or queued, else returns false.\r
8479      * @return {Boolean} True if element has active effects, else false\r
8480      */\r
8481     hasActiveFx : function(){\r
8482         return getQueue(this.dom.id)[0];\r
8483     },\r
8484 \r
8485     /**\r
8486      * Stops any running effects and clears the element's internal effects queue if it contains\r
8487      * any additional effects that haven't started yet.\r
8488      * @return {Ext.Element} The Element\r
8489      */\r
8490     stopFx : function(finish){\r
8491         var me = this,\r
8492             id = me.dom.id;\r
8493         if(me.hasActiveFx()){\r
8494             var cur = getQueue(id)[0];\r
8495             if(cur && cur.anim){\r
8496                 if(cur.anim.isAnimated){\r
8497                     setQueue(id, [cur]); //clear\r
8498                     cur.anim.stop(finish !== undefined ? finish : TRUE);\r
8499                 }else{\r
8500                     setQueue(id, []);\r
8501                 }\r
8502             }\r
8503         }\r
8504         return me;\r
8505     },\r
8506 \r
8507     /* @private */\r
8508     beforeFx : function(o){\r
8509         if(this.hasActiveFx() && !o.concurrent){\r
8510            if(o.stopFx){\r
8511                this.stopFx();\r
8512                return TRUE;\r
8513            }\r
8514            return FALSE;\r
8515         }\r
8516         return TRUE;\r
8517     },\r
8518 \r
8519     /**\r
8520      * Returns true if the element is currently blocking so that no other effect can be queued\r
8521      * until this effect is finished, else returns false if blocking is not set.  This is commonly\r
8522      * used to ensure that an effect initiated by a user action runs to completion prior to the\r
8523      * same effect being restarted (e.g., firing only one effect even if the user clicks several times).\r
8524      * @return {Boolean} True if blocking, else false\r
8525      */\r
8526     hasFxBlock : function(){\r
8527         var q = getQueue(this.dom.id);\r
8528         return q && q[0] && q[0].block;\r
8529     },\r
8530 \r
8531     /* @private */\r
8532     queueFx : function(o, fn){\r
8533         var me = fly(this.dom);\r
8534         if(!me.hasFxBlock()){\r
8535             Ext.applyIf(o, me.fxDefaults);\r
8536             if(!o.concurrent){\r
8537                 var run = me.beforeFx(o);\r
8538                 fn.block = o.block;\r
8539                 getQueue(me.dom.id).push(fn);\r
8540                 if(run){\r
8541                     me.nextFx();\r
8542                 }\r
8543             }else{\r
8544                 fn.call(me);\r
8545             }\r
8546         }\r
8547         return me;\r
8548     },\r
8549 \r
8550     /* @private */\r
8551     fxWrap : function(pos, o, vis){ \r
8552         var dom = this.dom,\r
8553             wrap,\r
8554             wrapXY;\r
8555         if(!o.wrap || !(wrap = Ext.getDom(o.wrap))){            \r
8556             if(o.fixPosition){\r
8557                 wrapXY = fly(dom).getXY();\r
8558             }\r
8559             var div = document.createElement("div");\r
8560             div.style.visibility = vis;\r
8561             wrap = dom.parentNode.insertBefore(div, dom);\r
8562             fly(wrap).setPositioning(pos);\r
8563             if(fly(wrap).isStyle(POSITION, "static")){\r
8564                 fly(wrap).position("relative");\r
8565             }\r
8566             fly(dom).clearPositioning('auto');\r
8567             fly(wrap).clip();\r
8568             wrap.appendChild(dom);\r
8569             if(wrapXY){\r
8570                 fly(wrap).setXY(wrapXY);\r
8571             }\r
8572         }\r
8573         return wrap;\r
8574     },\r
8575 \r
8576     /* @private */\r
8577     fxUnwrap : function(wrap, pos, o){      \r
8578         var dom = this.dom;\r
8579         fly(dom).clearPositioning();\r
8580         fly(dom).setPositioning(pos);\r
8581         if(!o.wrap){\r
8582             var pn = fly(wrap).dom.parentNode;
8583             pn.insertBefore(dom, wrap); \r
8584             fly(wrap).remove();\r
8585         }\r
8586     },\r
8587 \r
8588     /* @private */\r
8589     getFxRestore : function(){\r
8590         var st = this.dom.style;\r
8591         return {pos: this.getPositioning(), width: st.width, height : st.height};\r
8592     },\r
8593 \r
8594     /* @private */\r
8595     afterFx : function(o){\r
8596         var dom = this.dom,\r
8597             id = dom.id;\r
8598         if(o.afterStyle){\r
8599             fly(dom).setStyle(o.afterStyle);            \r
8600         }\r
8601         if(o.afterCls){\r
8602             fly(dom).addClass(o.afterCls);\r
8603         }\r
8604         if(o.remove == TRUE){\r
8605             fly(dom).remove();\r
8606         }\r
8607         if(o.callback){\r
8608             o.callback.call(o.scope, fly(dom));\r
8609         }\r
8610         if(!o.concurrent){\r
8611             getQueue(id).shift();\r
8612             fly(dom).nextFx();\r
8613         }\r
8614     },\r
8615 \r
8616     /* @private */\r
8617     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){\r
8618         animType = animType || 'run';\r
8619         opt = opt || {};\r
8620         var anim = Ext.lib.Anim[animType](\r
8621                 this.dom, \r
8622                 args,\r
8623                 (opt.duration || defaultDur) || .35,\r
8624                 (opt.easing || defaultEase) || EASEOUT,\r
8625                 cb,            \r
8626                 this\r
8627             );\r
8628         opt.anim = anim;\r
8629         return anim;\r
8630     }\r
8631 };\r
8632 \r
8633 // backwards compat\r
8634 Ext.Fx.resize = Ext.Fx.scale;\r
8635 \r
8636 //When included, Ext.Fx is automatically applied to Element so that all basic\r
8637 //effects are available directly via the Element API\r
8638 Ext.Element.addMethods(Ext.Fx);\r
8639 })();
8640 /**\r
8641  * @class Ext.CompositeElementLite\r
8642  * <p>This class encapsulates a <i>collection</i> of DOM elements, providing methods to filter\r
8643  * members, or to perform collective actions upon the whole set.</p>\r
8644  * <p>Although they are not listed, this class supports all of the methods of {@link Ext.Element} and\r
8645  * {@link Ext.Fx}. The methods from these classes will be performed on all the elements in this collection.</p>\r
8646  * Example:<pre><code>\r
8647 var els = Ext.select("#some-el div.some-class");\r
8648 // or select directly from an existing element\r
8649 var el = Ext.get('some-el');\r
8650 el.select('div.some-class');\r
8651 \r
8652 els.setWidth(100); // all elements become 100 width\r
8653 els.hide(true); // all elements fade out and hide\r
8654 // or\r
8655 els.setWidth(100).hide(true);\r
8656 </code>\r
8657  */\r
8658 Ext.CompositeElementLite = function(els, root){\r
8659     /**\r
8660      * <p>The Array of DOM elements which this CompositeElement encapsulates. Read-only.</p>\r
8661      * <p>This will not <i>usually</i> be accessed in developers' code, but developers wishing\r
8662      * to augment the capabilities of the CompositeElementLite class may use it when adding\r
8663      * methods to the class.</p>\r
8664      * <p>For example to add the <code>nextAll</code> method to the class to <b>add</b> all\r
8665      * following siblings of selected elements, the code would be</p><code><pre>\r
8666 Ext.override(Ext.CompositeElementLite, {\r
8667     nextAll: function() {\r
8668         var els = this.elements, i, l = els.length, n, r = [], ri = -1;\r
8669 \r
8670 //      Loop through all elements in this Composite, accumulating\r
8671 //      an Array of all siblings.\r
8672         for (i = 0; i < l; i++) {\r
8673             for (n = els[i].nextSibling; n; n = n.nextSibling) {\r
8674                 r[++ri] = n;\r
8675             }\r
8676         }\r
8677 \r
8678 //      Add all found siblings to this Composite\r
8679         return this.add(r);\r
8680     }\r
8681 });</pre></code>\r
8682      * @type Array\r
8683      * @property elements\r
8684      */\r
8685     this.elements = [];\r
8686     this.add(els, root);\r
8687     this.el = new Ext.Element.Flyweight();\r
8688 };\r
8689 \r
8690 Ext.CompositeElementLite.prototype = {\r
8691     isComposite: true,    \r
8692     \r
8693     // private\r
8694     getElement : function(el){\r
8695         // Set the shared flyweight dom property to the current element\r
8696         var e = this.el;\r
8697         e.dom = el;\r
8698         e.id = el.id;\r
8699         return e;\r
8700     },\r
8701     \r
8702     // private\r
8703     transformElement : function(el){\r
8704         return Ext.getDom(el);\r
8705     },\r
8706     \r
8707     /**\r
8708      * Returns the number of elements in this Composite.\r
8709      * @return Number\r
8710      */\r
8711     getCount : function(){\r
8712         return this.elements.length;\r
8713     },    \r
8714     /**\r
8715      * Adds elements to this Composite object.\r
8716      * @param {Mixed} els Either an Array of DOM elements to add, or another Composite object who's elements should be added.\r
8717      * @return {CompositeElement} This Composite object.\r
8718      */\r
8719     add : function(els, root){\r
8720         var me = this,\r
8721             elements = me.elements;\r
8722         if(!els){\r
8723             return this;\r
8724         }\r
8725         if(Ext.isString(els)){\r
8726             els = Ext.Element.selectorFunction(els, root);\r
8727         }else if(els.isComposite){\r
8728             els = els.elements;\r
8729         }else if(!Ext.isIterable(els)){\r
8730             els = [els];\r
8731         }\r
8732         \r
8733         for(var i = 0, len = els.length; i < len; ++i){\r
8734             elements.push(me.transformElement(els[i]));\r
8735         }\r
8736         return me;\r
8737     },\r
8738     \r
8739     invoke : function(fn, args){\r
8740         var me = this,\r
8741             els = me.elements,\r
8742             len = els.length, \r
8743             e;\r
8744             \r
8745         for(i = 0; i<len; i++) {\r
8746             e = els[i];\r
8747             if(e){\r
8748                 Ext.Element.prototype[fn].apply(me.getElement(e), args);\r
8749             }\r
8750         }\r
8751         return me;\r
8752     },\r
8753     /**\r
8754      * Returns a flyweight Element of the dom element object at the specified index\r
8755      * @param {Number} index\r
8756      * @return {Ext.Element}\r
8757      */\r
8758     item : function(index){\r
8759         var me = this,\r
8760             el = me.elements[index],\r
8761             out = null;\r
8762 \r
8763         if(el){\r
8764             out = me.getElement(el);\r
8765         }\r
8766         return out;\r
8767     },\r
8768 \r
8769     // fixes scope with flyweight\r
8770     addListener : function(eventName, handler, scope, opt){\r
8771         var els = this.elements,\r
8772             len = els.length,\r
8773             i, e;\r
8774         \r
8775         for(i = 0; i<len; i++) {\r
8776             e = els[i];\r
8777             if(e) {\r
8778                 Ext.EventManager.on(e, eventName, handler, scope || e, opt);\r
8779             }\r
8780         }\r
8781         return this;\r
8782     },\r
8783     /**\r
8784      * <p>Calls the passed function for each element in this composite.</p>\r
8785      * @param {Function} fn The function to call. The function is passed the following parameters:<ul>\r
8786      * <li><b>el</b> : Element<div class="sub-desc">The current Element in the iteration.\r
8787      * <b>This is the flyweight (shared) Ext.Element instance, so if you require a\r
8788      * a reference to the dom node, use el.dom.</b></div></li>\r
8789      * <li><b>c</b> : Composite<div class="sub-desc">This Composite object.</div></li>\r
8790      * <li><b>idx</b> : Number<div class="sub-desc">The zero-based index in the iteration.</div></li>\r
8791      * </ul>\r
8792      * @param {Object} scope (optional) The scope (<i>this</i> reference) in which the function is executed. (defaults to the Element)\r
8793      * @return {CompositeElement} this\r
8794      */\r
8795     each : function(fn, scope){       \r
8796         var me = this,\r
8797             els = me.elements,\r
8798             len = els.length,\r
8799             i, e;\r
8800         \r
8801         for(i = 0; i<len; i++) {\r
8802             e = els[i];\r
8803             if(e){\r
8804                 e = this.getElement(e);\r
8805                 if(fn.call(scope || e, e, me, i)){\r
8806                     break;\r
8807                 }\r
8808             }\r
8809         }\r
8810         return me;\r
8811     },\r
8812     \r
8813     /**\r
8814     * Clears this Composite and adds the elements passed.\r
8815     * @param {Mixed} els Either an array of DOM elements, or another Composite from which to fill this Composite.\r
8816     * @return {CompositeElement} this\r
8817     */\r
8818     fill : function(els){\r
8819         var me = this;\r
8820         me.elements = [];\r
8821         me.add(els);\r
8822         return me;\r
8823     },\r
8824     \r
8825     /**\r
8826      * Filters this composite to only elements that match the passed selector.\r
8827      * @param {String/Function} selector A string CSS selector or a comparison function.\r
8828      * The comparison function will be called with the following arguments:<ul>\r
8829      * <li><code>el</code> : Ext.Element<div class="sub-desc">The current DOM element.</div></li>\r
8830      * <li><code>index</code> : Number<div class="sub-desc">The current index within the collection.</div></li>\r
8831      * </ul>\r
8832      * @return {CompositeElement} this\r
8833      */\r
8834     filter : function(selector){\r
8835         var els = [],\r
8836             me = this,\r
8837             elements = me.elements,\r
8838             fn = Ext.isFunction(selector) ? selector\r
8839                 : function(el){\r
8840                     return el.is(selector);\r
8841                 };\r
8842                 \r
8843         \r
8844         me.each(function(el, self, i){\r
8845             if(fn(el, i) !== false){\r
8846                 els[els.length] = me.transformElement(el);\r
8847             }\r
8848         });\r
8849         me.elements = els;\r
8850         return me;\r
8851     },\r
8852     \r
8853     /**\r
8854      * Find the index of the passed element within the composite collection.\r
8855      * @param el {Mixed} The id of an element, or an Ext.Element, or an HtmlElement to find within the composite collection.\r
8856      * @return Number The index of the passed Ext.Element in the composite collection, or -1 if not found.\r
8857      */\r
8858     indexOf : function(el){\r
8859         return this.elements.indexOf(this.transformElement(el));\r
8860     },\r
8861     \r
8862     /**\r
8863     * Replaces the specified element with the passed element.\r
8864     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite\r
8865     * to replace.\r
8866     * @param {Mixed} replacement The id of an element or the Element itself.\r
8867     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.\r
8868     * @return {CompositeElement} this\r
8869     */    \r
8870     replaceElement : function(el, replacement, domReplace){\r
8871         var index = !isNaN(el) ? el : this.indexOf(el),\r
8872             d;\r
8873         if(index > -1){\r
8874             replacement = Ext.getDom(replacement);\r
8875             if(domReplace){\r
8876                 d = this.elements[index];\r
8877                 d.parentNode.insertBefore(replacement, d);\r
8878                 Ext.removeNode(d);\r
8879             }\r
8880             this.elements.splice(index, 1, replacement);\r
8881         }\r
8882         return this;\r
8883     },\r
8884     \r
8885     /**\r
8886      * Removes all elements.\r
8887      */\r
8888     clear : function(){\r
8889         this.elements = [];\r
8890     }\r
8891 };\r
8892 \r
8893 Ext.CompositeElementLite.prototype.on = Ext.CompositeElementLite.prototype.addListener;\r
8894 \r
8895 (function(){\r
8896 var fnName,\r
8897     ElProto = Ext.Element.prototype,\r
8898     CelProto = Ext.CompositeElementLite.prototype;\r
8899     \r
8900 for(fnName in ElProto){\r
8901     if(Ext.isFunction(ElProto[fnName])){\r
8902         (function(fnName){ \r
8903             CelProto[fnName] = CelProto[fnName] || function(){\r
8904                 return this.invoke(fnName, arguments);\r
8905             };\r
8906         }).call(CelProto, fnName);\r
8907         \r
8908     }\r
8909 }\r
8910 })();\r
8911 \r
8912 if(Ext.DomQuery){\r
8913     Ext.Element.selectorFunction = Ext.DomQuery.select;\r
8914\r
8915 \r
8916 /**\r
8917  * Selects elements based on the passed CSS selector to enable {@link Ext.Element Element} methods\r
8918  * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or\r
8919  * {@link Ext.CompositeElementLite CompositeElementLite} object.\r
8920  * @param {String/Array} selector The CSS selector or an array of elements\r
8921  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root\r
8922  * @return {CompositeElementLite/CompositeElement}\r
8923  * @member Ext.Element\r
8924  * @method select\r
8925  */\r
8926 Ext.Element.select = function(selector, root){\r
8927     var els;\r
8928     if(typeof selector == "string"){\r
8929         els = Ext.Element.selectorFunction(selector, root);\r
8930     }else if(selector.length !== undefined){\r
8931         els = selector;\r
8932     }else{\r
8933         throw "Invalid selector";\r
8934     }\r
8935     return new Ext.CompositeElementLite(els);\r
8936 };\r
8937 /**\r
8938  * Selects elements based on the passed CSS selector to enable {@link Ext.Element Element} methods\r
8939  * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or\r
8940  * {@link Ext.CompositeElementLite CompositeElementLite} object.\r
8941  * @param {String/Array} selector The CSS selector or an array of elements\r
8942  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root\r
8943  * @return {CompositeElementLite/CompositeElement}\r
8944  * @member Ext\r
8945  * @method select\r
8946  */\r
8947 Ext.select = Ext.Element.select;/**\r
8948  * @class Ext.CompositeElementLite\r
8949  */\r
8950 Ext.apply(Ext.CompositeElementLite.prototype, { \r
8951         addElements : function(els, root){\r
8952         if(!els){\r
8953             return this;\r
8954         }\r
8955         if(typeof els == "string"){\r
8956             els = Ext.Element.selectorFunction(els, root);\r
8957         }\r
8958         var yels = this.elements;        \r
8959             Ext.each(els, function(e) {\r
8960                 yels.push(Ext.get(e));\r
8961         });\r
8962         return this;\r
8963     },\r
8964     \r
8965     /**\r
8966      * Returns the first Element\r
8967      * @return {Ext.Element}\r
8968      */\r
8969     first : function(){\r
8970         return this.item(0);\r
8971     },   \r
8972     \r
8973     /**\r
8974      * Returns the last Element\r
8975      * @return {Ext.Element}\r
8976      */\r
8977     last : function(){\r
8978         return this.item(this.getCount()-1);\r
8979     },\r
8980     \r
8981     /**\r
8982      * Returns true if this composite contains the passed element\r
8983      * @param el {Mixed} The id of an element, or an Ext.Element, or an HtmlElement to find within the composite collection.\r
8984      * @return Boolean\r
8985      */\r
8986     contains : function(el){\r
8987         return this.indexOf(el) != -1;\r
8988     },
8989     
8990     /**\r
8991     * Removes the specified element(s).\r
8992     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite\r
8993     * or an array of any of those.\r
8994     * @param {Boolean} removeDom (optional) True to also remove the element from the document\r
8995     * @return {CompositeElement} this\r
8996     */\r
8997     removeElement : function(keys, removeDom){\r
8998         var me = this,\r
8999                 els = this.elements,        \r
9000                 el;             \r
9001             Ext.each(keys, function(val){\r
9002                     if ((el = (els[val] || els[val = me.indexOf(val)]))) {\r
9003                         if(removeDom){\r
9004                     if(el.dom){\r
9005                         el.remove();\r
9006                     }else{\r
9007                         Ext.removeNode(el);\r
9008                     }\r
9009                 }\r
9010                         els.splice(val, 1);                     \r
9011                         }\r
9012             });\r
9013         return this;\r
9014     }    \r
9015 });
9016 /**\r
9017  * @class Ext.CompositeElement\r
9018  * @extends Ext.CompositeElementLite\r
9019  * <p>This class encapsulates a <i>collection</i> of DOM elements, providing methods to filter\r
9020  * members, or to perform collective actions upon the whole set.</p>\r
9021  * <p>Although they are not listed, this class supports all of the methods of {@link Ext.Element} and\r
9022  * {@link Ext.Fx}. The methods from these classes will be performed on all the elements in this collection.</p>\r
9023  * <p>All methods return <i>this</i> and can be chained.</p>\r
9024  * Usage:\r
9025 <pre><code>\r
9026 var els = Ext.select("#some-el div.some-class", true);\r
9027 // or select directly from an existing element\r
9028 var el = Ext.get('some-el');\r
9029 el.select('div.some-class', true);\r
9030 \r
9031 els.setWidth(100); // all elements become 100 width\r
9032 els.hide(true); // all elements fade out and hide\r
9033 // or\r
9034 els.setWidth(100).hide(true);\r
9035 </code></pre>\r
9036  */\r
9037 Ext.CompositeElement = function(els, root){\r
9038     this.elements = [];\r
9039     this.add(els, root);\r
9040 };\r
9041 \r
9042 Ext.extend(Ext.CompositeElement, Ext.CompositeElementLite, {\r
9043     \r
9044     // private\r
9045     getElement : function(el){\r
9046         // In this case just return it, since we already have a reference to it\r
9047         return el;\r
9048     },\r
9049     \r
9050     // private\r
9051     transformElement : function(el){\r
9052         return Ext.get(el);\r
9053     }\r
9054 \r
9055     /**\r
9056     * Adds elements to this composite.\r
9057     * @param {String/Array} els A string CSS selector, an array of elements or an element\r
9058     * @return {CompositeElement} this\r
9059     */\r
9060 \r
9061     /**\r
9062      * Returns the Element object at the specified index\r
9063      * @param {Number} index\r
9064      * @return {Ext.Element}\r
9065      */\r
9066 \r
9067     /**\r
9068      * Iterates each <code>element</code> in this <code>composite</code>\r
9069      * calling the supplied function using {@link Ext#each}.\r
9070      * @param {Function} fn The function to be called with each\r
9071      * <code>element</code>. If the supplied function returns <tt>false</tt>,\r
9072      * iteration stops. This function is called with the following arguments:\r
9073      * <div class="mdetail-params"><ul>\r
9074      * <li><code>element</code> : <i>Ext.Element</i><div class="sub-desc">The element at the current <code>index</code>\r
9075      * in the <code>composite</code></div></li>\r
9076      * <li><code>composite</code> : <i>Object</i> <div class="sub-desc">This composite.</div></li>\r
9077      * <li><code>index</code> : <i>Number</i> <div class="sub-desc">The current index within the <code>composite</code> </div></li>\r
9078      * </ul></div>\r
9079      * @param {Object} scope (optional) The scope (<code><this</code> reference) in which the specified function is executed.\r
9080      * Defaults to the <code>element</code> at the current <code>index</code>\r
9081      * within the composite.\r
9082      * @return {CompositeElement} this\r
9083      */\r
9084 });\r
9085 \r
9086 /**\r
9087  * Selects elements based on the passed CSS selector to enable {@link Ext.Element Element} methods\r
9088  * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or\r
9089  * {@link Ext.CompositeElementLite CompositeElementLite} object.\r
9090  * @param {String/Array} selector The CSS selector or an array of elements\r
9091  * @param {Boolean} unique (optional) true to create a unique Ext.Element for each element (defaults to a shared flyweight object)\r
9092  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root\r
9093  * @return {CompositeElementLite/CompositeElement}\r
9094  * @member Ext.Element\r
9095  * @method select\r
9096  */\r
9097 Ext.Element.select = function(selector, unique, root){\r
9098     var els;\r
9099     if(typeof selector == "string"){\r
9100         els = Ext.Element.selectorFunction(selector, root);\r
9101     }else if(selector.length !== undefined){\r
9102         els = selector;\r
9103     }else{\r
9104         throw "Invalid selector";\r
9105     }\r
9106 \r
9107     return (unique === true) ? new Ext.CompositeElement(els) : new Ext.CompositeElementLite(els);\r
9108 };\r
9109 \r
9110 /**\r
9111  * Selects elements based on the passed CSS selector to enable {@link Ext.Element Element} methods\r
9112  * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or\r
9113  * {@link Ext.CompositeElementLite CompositeElementLite} object.\r
9114  * @param {String/Array} selector The CSS selector or an array of elements\r
9115  * @param {Boolean} unique (optional) true to create a unique Ext.Element for each element (defaults to a shared flyweight object)\r
9116  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root\r
9117  * @return {CompositeElementLite/CompositeElement}\r
9118  * @member Ext.Element\r
9119  * @method select\r
9120  */\r
9121 Ext.select = Ext.Element.select;(function(){\r
9122     var BEFOREREQUEST = "beforerequest",\r
9123         REQUESTCOMPLETE = "requestcomplete",\r
9124         REQUESTEXCEPTION = "requestexception",\r
9125         UNDEFINED = undefined,\r
9126         LOAD = 'load',\r
9127         POST = 'POST',\r
9128         GET = 'GET',\r
9129         WINDOW = window;\r
9130     \r
9131     /**\r
9132      * @class Ext.data.Connection\r
9133      * @extends Ext.util.Observable\r
9134      * <p>The class encapsulates a connection to the page's originating domain, allowing requests to be made\r
9135      * either to a configured URL, or to a URL specified at request time.</p>\r
9136      * <p>Requests made by this class are asynchronous, and will return immediately. No data from\r
9137      * the server will be available to the statement immediately following the {@link #request} call.\r
9138      * To process returned data, use a\r
9139      * <a href="#request-option-success" ext:member="request-option-success" ext:cls="Ext.data.Connection">success callback</a>\r
9140      * in the request options object,\r
9141      * or an {@link #requestcomplete event listener}.</p>\r
9142      * <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
9143      * is they are <b>not</b> performed using XMLHttpRequests. Instead the form is submitted in the standard\r
9144      * manner with the DOM <tt>&lt;form></tt> element temporarily modified to have its\r
9145      * <a href="http://www.w3.org/TR/REC-html40/present/frames.html#adef-target">target</a> set to refer\r
9146      * to a dynamically generated, hidden <tt>&lt;iframe></tt> which is inserted into the document\r
9147      * but removed after the return data has been gathered.</p>\r
9148      * <p>The server response is parsed by the browser to create the document for the IFRAME. If the\r
9149      * server is using JSON to send the return object, then the\r
9150      * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.17">Content-Type</a> header\r
9151      * must be set to "text/html" in order to tell the browser to insert the text unchanged into the document body.</p>\r
9152      * <p>Characters which are significant to an HTML parser must be sent as HTML entities, so encode\r
9153      * "&lt;" as "&amp;lt;", "&amp;" as "&amp;amp;" etc.</p>\r
9154      * <p>The response text is retrieved from the document, and a fake XMLHttpRequest object\r
9155      * is created containing a <tt>responseText</tt> property in order to conform to the\r
9156      * requirements of event handlers and callbacks.</p>\r
9157      * <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
9158      * and some server technologies (notably JEE) may require some custom processing in order to\r
9159      * retrieve parameter names and parameter values from the packet content.</p>\r
9160      * @constructor\r
9161      * @param {Object} config a configuration object.\r
9162      */\r
9163     Ext.data.Connection = function(config){    \r
9164         Ext.apply(this, config);\r
9165         this.addEvents(\r
9166             /**\r
9167              * @event beforerequest\r
9168              * Fires before a network request is made to retrieve a data object.\r
9169              * @param {Connection} conn This Connection object.\r
9170              * @param {Object} options The options config object passed to the {@link #request} method.\r
9171              */\r
9172             BEFOREREQUEST,\r
9173             /**\r
9174              * @event requestcomplete\r
9175              * Fires if the request was successfully completed.\r
9176              * @param {Connection} conn This Connection object.\r
9177              * @param {Object} response The XHR object containing the response data.\r
9178              * See <a href="http://www.w3.org/TR/XMLHttpRequest/">The XMLHttpRequest Object</a>\r
9179              * for details.\r
9180              * @param {Object} options The options config object passed to the {@link #request} method.\r
9181              */\r
9182             REQUESTCOMPLETE,\r
9183             /**\r
9184              * @event requestexception\r
9185              * Fires if an error HTTP status was returned from the server.\r
9186              * See <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html">HTTP Status Code Definitions</a>\r
9187              * for details of HTTP status codes.\r
9188              * @param {Connection} conn This Connection object.\r
9189              * @param {Object} response The XHR object containing the response data.\r
9190              * See <a href="http://www.w3.org/TR/XMLHttpRequest/">The XMLHttpRequest Object</a>\r
9191              * for details.\r
9192              * @param {Object} options The options config object passed to the {@link #request} method.\r
9193              */\r
9194             REQUESTEXCEPTION\r
9195         );\r
9196         Ext.data.Connection.superclass.constructor.call(this);\r
9197     };\r
9198 \r
9199     Ext.extend(Ext.data.Connection, Ext.util.Observable, {\r
9200         /**\r
9201          * @cfg {String} url (Optional) <p>The default URL to be used for requests to the server. Defaults to undefined.</p>\r
9202          * <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
9203          * (<code><b>this</b></code> reference) of the function is the <code>scope</code> option passed to the {@link #request} method.</p>\r
9204          */\r
9205         /**\r
9206          * @cfg {Object} extraParams (Optional) An object containing properties which are used as\r
9207          * extra parameters to each request made by this object. (defaults to undefined)\r
9208          */\r
9209         /**\r
9210          * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added\r
9211          *  to each request made by this object. (defaults to undefined)\r
9212          */\r
9213         /**\r
9214          * @cfg {String} method (Optional) The default HTTP method to be used for requests.\r
9215          * (defaults to undefined; if not set, but {@link #request} params are present, POST will be used;\r
9216          * otherwise, GET will be used.)\r
9217          */\r
9218         /**\r
9219          * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)\r
9220          */\r
9221         timeout : 30000,\r
9222         /**\r
9223          * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)\r
9224          * @type Boolean\r
9225          */\r
9226         autoAbort:false,\r
9227     \r
9228         /**\r
9229          * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)\r
9230          * @type Boolean\r
9231          */\r
9232         disableCaching: true,\r
9233         \r
9234         /**\r
9235          * @cfg {String} disableCachingParam (Optional) Change the parameter which is sent went disabling caching\r
9236          * through a cache buster. Defaults to '_dc'\r
9237          * @type String\r
9238          */\r
9239         disableCachingParam: '_dc',\r
9240         \r
9241         /**\r
9242          * <p>Sends an HTTP request to a remote server.</p>\r
9243          * <p><b>Important:</b> Ajax server requests are asynchronous, and this call will\r
9244          * return before the response has been received. Process any returned data\r
9245          * in a callback function.</p>\r
9246          * <pre><code>\r
9247 Ext.Ajax.request({\r
9248    url: 'ajax_demo/sample.json',\r
9249    success: function(response, opts) {\r
9250       var obj = Ext.decode(response.responseText);\r
9251       console.dir(obj);\r
9252    },\r
9253    failure: function(response, opts) {\r
9254       console.log('server-side failure with status code ' + response.status);\r
9255    }\r
9256 });\r
9257          * </code></pre>\r
9258          * <p>To execute a callback function in the correct scope, use the <tt>scope</tt> option.</p>\r
9259          * @param {Object} options An object which may contain the following properties:<ul>\r
9260          * <li><b>url</b> : String/Function (Optional)<div class="sub-desc">The URL to\r
9261          * which to send the request, or a function to call which returns a URL string. The scope of the\r
9262          * function is specified by the <tt>scope</tt> option. Defaults to the configured\r
9263          * <tt>{@link #url}</tt>.</div></li>\r
9264          * <li><b>params</b> : Object/String/Function (Optional)<div class="sub-desc">\r
9265          * An object containing properties which are used as parameters to the\r
9266          * request, a url encoded string or a function to call to get either. The scope of the function\r
9267          * is specified by the <tt>scope</tt> option.</div></li>\r
9268          * <li><b>method</b> : String (Optional)<div class="sub-desc">The HTTP method to use\r
9269          * for the request. Defaults to the configured method, or if no method was configured,\r
9270          * "GET" if no parameters are being sent, and "POST" if parameters are being sent.  Note that\r
9271          * the method name is case-sensitive and should be all caps.</div></li>\r
9272          * <li><b>callback</b> : Function (Optional)<div class="sub-desc">The\r
9273          * function to be called upon receipt of the HTTP response. The callback is\r
9274          * called regardless of success or failure and is passed the following\r
9275          * parameters:<ul>\r
9276          * <li><b>options</b> : Object<div class="sub-desc">The parameter to the request call.</div></li>\r
9277          * <li><b>success</b> : Boolean<div class="sub-desc">True if the request succeeded.</div></li>\r
9278          * <li><b>response</b> : Object<div class="sub-desc">The XMLHttpRequest object containing the response data. \r
9279          * See <a href="http://www.w3.org/TR/XMLHttpRequest/">http://www.w3.org/TR/XMLHttpRequest/</a> for details about \r
9280          * accessing elements of the response.</div></li>\r
9281          * </ul></div></li>\r
9282          * <li><a id="request-option-success"></a><b>success</b> : Function (Optional)<div class="sub-desc">The function\r
9283          * to be called upon success of the request. The callback is passed the following\r
9284          * parameters:<ul>\r
9285          * <li><b>response</b> : Object<div class="sub-desc">The XMLHttpRequest object containing the response data.</div></li>\r
9286          * <li><b>options</b> : Object<div class="sub-desc">The parameter to the request call.</div></li>\r
9287          * </ul></div></li>\r
9288          * <li><b>failure</b> : Function (Optional)<div class="sub-desc">The function\r
9289          * to be called upon failure of the request. The callback is passed the\r
9290          * following parameters:<ul>\r
9291          * <li><b>response</b> : Object<div class="sub-desc">The XMLHttpRequest object containing the response data.</div></li>\r
9292          * <li><b>options</b> : Object<div class="sub-desc">The parameter to the request call.</div></li>\r
9293          * </ul></div></li>\r
9294          * <li><b>scope</b> : Object (Optional)<div class="sub-desc">The scope in\r
9295          * which to execute the callbacks: The "this" object for the callback function. If the <tt>url</tt>, or <tt>params</tt> options were\r
9296          * specified as functions from which to draw values, then this also serves as the scope for those function calls.\r
9297          * Defaults to the browser window.</div></li>\r
9298          * <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
9299          * <li><b>form</b> : Element/HTMLElement/String (Optional)<div class="sub-desc">The <tt>&lt;form&gt;</tt>\r
9300          * Element or the id of the <tt>&lt;form&gt;</tt> to pull parameters from.</div></li>\r
9301          * <li><a id="request-option-isUpload"></a><b>isUpload</b> : Boolean (Optional)<div class="sub-desc"><b>Only meaningful when used \r
9302          * with the <tt>form</tt> option</b>.\r
9303          * <p>True if the form object is a file upload (will be set automatically if the form was\r
9304          * configured with <b><tt>enctype</tt></b> "multipart/form-data").</p>\r
9305          * <p>File uploads are not performed using normal "Ajax" techniques, that is they are <b>not</b>\r
9306          * performed using XMLHttpRequests. Instead the form is submitted in the standard manner with the\r
9307          * DOM <tt>&lt;form></tt> element temporarily modified to have its\r
9308          * <a href="http://www.w3.org/TR/REC-html40/present/frames.html#adef-target">target</a> set to refer\r
9309          * to a dynamically generated, hidden <tt>&lt;iframe></tt> which is inserted into the document\r
9310          * but removed after the return data has been gathered.</p>\r
9311          * <p>The server response is parsed by the browser to create the document for the IFRAME. If the\r
9312          * server is using JSON to send the return object, then the\r
9313          * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.17">Content-Type</a> header\r
9314          * must be set to "text/html" in order to tell the browser to insert the text unchanged into the document body.</p>\r
9315          * <p>The response text is retrieved from the document, and a fake XMLHttpRequest object\r
9316          * is created containing a <tt>responseText</tt> property in order to conform to the\r
9317          * requirements of event handlers and callbacks.</p>\r
9318          * <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
9319          * and some server technologies (notably JEE) may require some custom processing in order to\r
9320          * retrieve parameter names and parameter values from the packet content.</p>\r
9321          * </div></li>\r
9322          * <li><b>headers</b> : Object (Optional)<div class="sub-desc">Request\r
9323          * headers to set for the request.</div></li>\r
9324          * <li><b>xmlData</b> : Object (Optional)<div class="sub-desc">XML document\r
9325          * to use for the post. Note: This will be used instead of params for the post\r
9326          * data. Any params will be appended to the URL.</div></li>\r
9327          * <li><b>jsonData</b> : Object/String (Optional)<div class="sub-desc">JSON\r
9328          * data to use as the post. Note: This will be used instead of params for the post\r
9329          * data. Any params will be appended to the URL.</div></li>\r
9330          * <li><b>disableCaching</b> : Boolean (Optional)<div class="sub-desc">True\r
9331          * to add a unique cache-buster param to GET requests.</div></li>\r
9332          * </ul></p>\r
9333          * <p>The options object may also contain any other property which might be needed to perform\r
9334          * postprocessing in a callback because it is passed to callback functions.</p>\r
9335          * @return {Number} transactionId The id of the server transaction. This may be used\r
9336          * to cancel the request.\r
9337          */\r
9338         request : function(o){\r
9339             var me = this;\r
9340             if(me.fireEvent(BEFOREREQUEST, me, o)){\r
9341                 if (o.el) {\r
9342                     if(!Ext.isEmpty(o.indicatorText)){\r
9343                         me.indicatorText = '<div class="loading-indicator">'+o.indicatorText+"</div>";\r
9344                     }\r
9345                     if(me.indicatorText) {\r
9346                         Ext.getDom(o.el).innerHTML = me.indicatorText;                        \r
9347                     }\r
9348                     o.success = (Ext.isFunction(o.success) ? o.success : function(){}).createInterceptor(function(response) {\r
9349                         Ext.getDom(o.el).innerHTML = response.responseText;\r
9350                     });\r
9351                 }\r
9352                 \r
9353                 var p = o.params,\r
9354                     url = o.url || me.url,                \r
9355                     method,\r
9356                     cb = {success: me.handleResponse,\r
9357                           failure: me.handleFailure,\r
9358                           scope: me,\r
9359                           argument: {options: o},\r
9360                           timeout : o.timeout || me.timeout\r
9361                     },\r
9362                     form,                    \r
9363                     serForm;                    \r
9364                   \r
9365                      \r
9366                 if (Ext.isFunction(p)) {\r
9367                     p = p.call(o.scope||WINDOW, o);\r
9368                 }\r
9369                                                            \r
9370                 p = Ext.urlEncode(me.extraParams, Ext.isObject(p) ? Ext.urlEncode(p) : p);    \r
9371                 \r
9372                 if (Ext.isFunction(url)) {\r
9373                     url = url.call(o.scope || WINDOW, o);\r
9374                 }\r
9375     \r
9376                 if((form = Ext.getDom(o.form))){\r
9377                     url = url || form.action;\r
9378                      if(o.isUpload || /multipart\/form-data/i.test(form.getAttribute("enctype"))) { \r
9379                          return me.doFormUpload.call(me, o, p, url);\r
9380                      }\r
9381                     serForm = Ext.lib.Ajax.serializeForm(form);                    \r
9382                     p = p ? (p + '&' + serForm) : serForm;\r
9383                 }\r
9384                 \r
9385                 method = o.method || me.method || ((p || o.xmlData || o.jsonData) ? POST : GET);\r
9386                 \r
9387                 if(method === GET && (me.disableCaching && o.disableCaching !== false) || o.disableCaching === true){\r
9388                     var dcp = o.disableCachingParam || me.disableCachingParam;\r
9389                     url = Ext.urlAppend(url, dcp + '=' + (new Date().getTime()));\r
9390                 }\r
9391                 \r
9392                 o.headers = Ext.apply(o.headers || {}, me.defaultHeaders || {});\r
9393                 \r
9394                 if(o.autoAbort === true || me.autoAbort) {\r
9395                     me.abort();\r
9396                 }\r
9397                  \r
9398                 if((method == GET || o.xmlData || o.jsonData) && p){\r
9399                     url = Ext.urlAppend(url, p);  \r
9400                     p = '';\r
9401                 }\r
9402                 return (me.transId = Ext.lib.Ajax.request(method, url, cb, p, o));\r
9403             }else{                \r
9404                 return o.callback ? o.callback.apply(o.scope, [o,UNDEFINED,UNDEFINED]) : null;\r
9405             }\r
9406         },\r
9407     \r
9408         /**\r
9409          * Determine whether this object has a request outstanding.\r
9410          * @param {Number} transactionId (Optional) defaults to the last transaction\r
9411          * @return {Boolean} True if there is an outstanding request.\r
9412          */\r
9413         isLoading : function(transId){\r
9414             return transId ? Ext.lib.Ajax.isCallInProgress(transId) : !! this.transId;            \r
9415         },\r
9416     \r
9417         /**\r
9418          * Aborts any outstanding request.\r
9419          * @param {Number} transactionId (Optional) defaults to the last transaction\r
9420          */\r
9421         abort : function(transId){\r
9422             if(transId || this.isLoading()){\r
9423                 Ext.lib.Ajax.abort(transId || this.transId);\r
9424             }\r
9425         },\r
9426 \r
9427         // private\r
9428         handleResponse : function(response){\r
9429             this.transId = false;\r
9430             var options = response.argument.options;\r
9431             response.argument = options ? options.argument : null;\r
9432             this.fireEvent(REQUESTCOMPLETE, this, response, options);\r
9433             if(options.success){\r
9434                 options.success.call(options.scope, response, options);\r
9435             }\r
9436             if(options.callback){\r
9437                 options.callback.call(options.scope, options, true, response);\r
9438             }\r
9439         },\r
9440 \r
9441         // private\r
9442         handleFailure : function(response, e){\r
9443             this.transId = false;\r
9444             var options = response.argument.options;\r
9445             response.argument = options ? options.argument : null;\r
9446             this.fireEvent(REQUESTEXCEPTION, this, response, options, e);\r
9447             if(options.failure){\r
9448                 options.failure.call(options.scope, response, options);\r
9449             }\r
9450             if(options.callback){\r
9451                 options.callback.call(options.scope, options, false, response);\r
9452             }\r
9453         },\r
9454 \r
9455         // private\r
9456         doFormUpload : function(o, ps, url){\r
9457             var id = Ext.id(),\r
9458                 doc = document,\r
9459                 frame = doc.createElement('iframe'),\r
9460                 form = Ext.getDom(o.form),\r
9461                 hiddens = [],\r
9462                 hd,\r
9463                 encoding = 'multipart/form-data',\r
9464                 buf = {\r
9465                     target: form.target,\r
9466                     method: form.method,\r
9467                     encoding: form.encoding,\r
9468                     enctype: form.enctype,\r
9469                     action: form.action\r
9470                 };\r
9471 \r
9472             Ext.fly(frame).set({\r
9473                 id: id,\r
9474                 name: id,\r
9475                 cls: 'x-hidden',\r
9476                 src: Ext.SSL_SECURE_URL // for IE\r
9477             });\r
9478             doc.body.appendChild(frame);\r
9479 \r
9480             // This is required so that IE doesn't pop the response up in a new window.\r
9481             if(Ext.isIE){\r
9482                document.frames[id].name = id;\r
9483             }\r
9484 \r
9485             Ext.fly(form).set({\r
9486                 target: id,\r
9487                 method: POST,\r
9488                 enctype: encoding,\r
9489                 encoding: encoding,\r
9490                 action: url || buf.action\r
9491             });\r
9492 \r
9493             // add dynamic params\r
9494             Ext.iterate(Ext.urlDecode(ps, false), function(k, v){\r
9495                 hd = doc.createElement('input');\r
9496                 Ext.fly(hd).set({\r
9497                     type: 'hidden',\r
9498                     value: v,\r
9499                     name: k\r
9500                 });\r
9501                 form.appendChild(hd);\r
9502                 hiddens.push(hd);\r
9503             });\r
9504 \r
9505             function cb(){\r
9506                 var me = this,\r
9507                     // bogus response object\r
9508                     r = {responseText : '',\r
9509                          responseXML : null,\r
9510                          argument : o.argument},\r
9511                     doc,\r
9512                     firstChild;\r
9513 \r
9514                 try{\r
9515                     doc = frame.contentWindow.document || frame.contentDocument || WINDOW.frames[id].document;\r
9516                     if(doc){\r
9517                         if(doc.body){\r
9518                             if(/textarea/i.test((firstChild = doc.body.firstChild || {}).tagName)){ // json response wrapped in textarea\r
9519                                 r.responseText = firstChild.value;\r
9520                             }else{\r
9521                                 r.responseText = doc.body.innerHTML;\r
9522                             }\r
9523                         }\r
9524                         //in IE the document may still have a body even if returns XML.\r
9525                         r.responseXML = doc.XMLDocument || doc;\r
9526                     }\r
9527                 }\r
9528                 catch(e) {}\r
9529 \r
9530                 Ext.EventManager.removeListener(frame, LOAD, cb, me);\r
9531 \r
9532                 me.fireEvent(REQUESTCOMPLETE, me, r, o);\r
9533 \r
9534                 function runCallback(fn, scope, args){\r
9535                     if(Ext.isFunction(fn)){\r
9536                         fn.apply(scope, args);\r
9537                     }\r
9538                 }\r
9539 \r
9540                 runCallback(o.success, o.scope, [r, o]);\r
9541                 runCallback(o.callback, o.scope, [o, true, r]);\r
9542 \r
9543                 if(!me.debugUploads){\r
9544                     setTimeout(function(){Ext.removeNode(frame);}, 100);\r
9545                 }\r
9546             }\r
9547 \r
9548             Ext.EventManager.on(frame, LOAD, cb, this);\r
9549             form.submit();\r
9550 \r
9551             Ext.fly(form).set(buf);\r
9552             Ext.each(hiddens, function(h) {\r
9553                 Ext.removeNode(h);\r
9554             });\r
9555         }\r
9556     });\r
9557 })();\r
9558 \r
9559 /**\r
9560  * @class Ext.Ajax\r
9561  * @extends Ext.data.Connection\r
9562  * <p>The global Ajax request class that provides a simple way to make Ajax requests\r
9563  * with maximum flexibility.</p>\r
9564  * <p>Since Ext.Ajax is a singleton, you can set common properties/events for it once\r
9565  * and override them at the request function level only if necessary.</p>\r
9566  * <p>Common <b>Properties</b> you may want to set are:<div class="mdetail-params"><ul>\r
9567  * <li><b><tt>{@link #method}</tt></b><p class="sub-desc"></p></li>\r
9568  * <li><b><tt>{@link #extraParams}</tt></b><p class="sub-desc"></p></li>\r
9569  * <li><b><tt>{@link #url}</tt></b><p class="sub-desc"></p></li>\r
9570  * </ul></div>\r
9571  * <pre><code>\r
9572 // Default headers to pass in every request\r
9573 Ext.Ajax.defaultHeaders = {\r
9574     'Powered-By': 'Ext'\r
9575 };\r
9576  * </code></pre> \r
9577  * </p>\r
9578  * <p>Common <b>Events</b> you may want to set are:<div class="mdetail-params"><ul>\r
9579  * <li><b><tt>{@link Ext.data.Connection#beforerequest beforerequest}</tt></b><p class="sub-desc"></p></li>\r
9580  * <li><b><tt>{@link Ext.data.Connection#requestcomplete requestcomplete}</tt></b><p class="sub-desc"></p></li>\r
9581  * <li><b><tt>{@link Ext.data.Connection#requestexception requestexception}</tt></b><p class="sub-desc"></p></li>\r
9582  * </ul></div>\r
9583  * <pre><code>\r
9584 // Example: show a spinner during all Ajax requests\r
9585 Ext.Ajax.on('beforerequest', this.showSpinner, this);\r
9586 Ext.Ajax.on('requestcomplete', this.hideSpinner, this);\r
9587 Ext.Ajax.on('requestexception', this.hideSpinner, this);\r
9588  * </code></pre> \r
9589  * </p>\r
9590  * <p>An example request:</p>\r
9591  * <pre><code>\r
9592 // Basic request\r
9593 Ext.Ajax.{@link Ext.data.Connection#request request}({\r
9594    url: 'foo.php',\r
9595    success: someFn,\r
9596    failure: otherFn,\r
9597    headers: {\r
9598        'my-header': 'foo'\r
9599    },\r
9600    params: { foo: 'bar' }\r
9601 });\r
9602 \r
9603 // Simple ajax form submission\r
9604 Ext.Ajax.{@link Ext.data.Connection#request request}({\r
9605     form: 'some-form',\r
9606     params: 'foo=bar'\r
9607 });\r
9608  * </code></pre> \r
9609  * </p>\r
9610  * @singleton\r
9611  */\r
9612 Ext.Ajax = new Ext.data.Connection({\r
9613     /**\r
9614      * @cfg {String} url @hide\r
9615      */\r
9616     /**\r
9617      * @cfg {Object} extraParams @hide\r
9618      */\r
9619     /**\r
9620      * @cfg {Object} defaultHeaders @hide\r
9621      */\r
9622     /**\r
9623      * @cfg {String} method (Optional) @hide\r
9624      */\r
9625     /**\r
9626      * @cfg {Number} timeout (Optional) @hide\r
9627      */\r
9628     /**\r
9629      * @cfg {Boolean} autoAbort (Optional) @hide\r
9630      */\r
9631 \r
9632     /**\r
9633      * @cfg {Boolean} disableCaching (Optional) @hide\r
9634      */\r
9635 \r
9636     /**\r
9637      * @property  disableCaching\r
9638      * True to add a unique cache-buster param to GET requests. (defaults to true)\r
9639      * @type Boolean\r
9640      */\r
9641     /**\r
9642      * @property  url\r
9643      * The default URL to be used for requests to the server. (defaults to undefined)\r
9644      * If the server receives all requests through one URL, setting this once is easier than\r
9645      * entering it on every request.\r
9646      * @type String\r
9647      */\r
9648     /**\r
9649      * @property  extraParams\r
9650      * An object containing properties which are used as extra parameters to each request made\r
9651      * by this object (defaults to undefined). Session information and other data that you need\r
9652      * to pass with each request are commonly put here.\r
9653      * @type Object\r
9654      */\r
9655     /**\r
9656      * @property  defaultHeaders\r
9657      * An object containing request headers which are added to each request made by this object\r
9658      * (defaults to undefined).\r
9659      * @type Object\r
9660      */\r
9661     /**\r
9662      * @property  method\r
9663      * The default HTTP method to be used for requests. Note that this is case-sensitive and\r
9664      * should be all caps (defaults to undefined; if not set but params are present will use\r
9665      * <tt>"POST"</tt>, otherwise will use <tt>"GET"</tt>.)\r
9666      * @type String\r
9667      */\r
9668     /**\r
9669      * @property  timeout\r
9670      * The timeout in milliseconds to be used for requests. (defaults to 30000)\r
9671      * @type Number\r
9672      */\r
9673 \r
9674     /**\r
9675      * @property  autoAbort\r
9676      * Whether a new request should abort any pending requests. (defaults to false)\r
9677      * @type Boolean\r
9678      */\r
9679     autoAbort : false,\r
9680 \r
9681     /**\r
9682      * Serialize the passed form into a url encoded string\r
9683      * @param {String/HTMLElement} form\r
9684      * @return {String}\r
9685      */\r
9686     serializeForm : function(form){\r
9687         return Ext.lib.Ajax.serializeForm(form);\r
9688     }\r
9689 });\r
9690 /**
9691  * @class Ext.Updater
9692  * @extends Ext.util.Observable
9693  * Provides AJAX-style update capabilities for Element objects.  Updater can be used to {@link #update}
9694  * an {@link Ext.Element} once, or you can use {@link #startAutoRefresh} to set up an auto-updating
9695  * {@link Ext.Element Element} on a specific interval.<br><br>
9696  * Usage:<br>
9697  * <pre><code>
9698  * var el = Ext.get("foo"); // Get Ext.Element object
9699  * var mgr = el.getUpdater();
9700  * mgr.update({
9701         url: "http://myserver.com/index.php",
9702         params: {
9703             param1: "foo",
9704             param2: "bar"
9705         }
9706  * });
9707  * ...
9708  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
9709  * <br>
9710  * // or directly (returns the same Updater instance)
9711  * var mgr = new Ext.Updater("myElementId");
9712  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
9713  * mgr.on("update", myFcnNeedsToKnow);
9714  * <br>
9715  * // short handed call directly from the element object
9716  * Ext.get("foo").load({
9717         url: "bar.php",
9718         scripts: true,
9719         params: "param1=foo&amp;param2=bar",
9720         text: "Loading Foo..."
9721  * });
9722  * </code></pre>
9723  * @constructor
9724  * Create new Updater directly.
9725  * @param {Mixed} el The element to update
9726  * @param {Boolean} forceNew (optional) By default the constructor checks to see if the passed element already
9727  * has an Updater and if it does it returns the same instance. This will skip that check (useful for extending this class).
9728  */
9729 Ext.UpdateManager = Ext.Updater = Ext.extend(Ext.util.Observable, 
9730 function() {
9731         var BEFOREUPDATE = "beforeupdate",
9732                 UPDATE = "update",
9733                 FAILURE = "failure";
9734                 
9735         // private
9736     function processSuccess(response){      
9737             var me = this;
9738         me.transaction = null;
9739         if (response.argument.form && response.argument.reset) {
9740             try { // put in try/catch since some older FF releases had problems with this
9741                 response.argument.form.reset();
9742             } catch(e){}
9743         }
9744         if (me.loadScripts) {
9745             me.renderer.render(me.el, response, me,
9746                updateComplete.createDelegate(me, [response]));
9747         } else {
9748             me.renderer.render(me.el, response, me);
9749             updateComplete.call(me, response);
9750         }
9751     }
9752     
9753     // private
9754     function updateComplete(response, type, success){
9755         this.fireEvent(type || UPDATE, this.el, response);
9756         if(Ext.isFunction(response.argument.callback)){
9757             response.argument.callback.call(response.argument.scope, this.el, Ext.isEmpty(success) ? true : false, response, response.argument.options);
9758         }
9759     }
9760
9761     // private
9762     function processFailure(response){              
9763         updateComplete.call(this, response, FAILURE, !!(this.transaction = null));
9764     }
9765             
9766         return {
9767             constructor: function(el, forceNew){
9768                     var me = this;
9769                 el = Ext.get(el);
9770                 if(!forceNew && el.updateManager){
9771                     return el.updateManager;
9772                 }
9773                 /**
9774                  * The Element object
9775                  * @type Ext.Element
9776                  */
9777                 me.el = el;
9778                 /**
9779                  * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
9780                  * @type String
9781                  */
9782                 me.defaultUrl = null;
9783         
9784                 me.addEvents(
9785                     /**
9786                      * @event beforeupdate
9787                      * Fired before an update is made, return false from your handler and the update is cancelled.
9788                      * @param {Ext.Element} el
9789                      * @param {String/Object/Function} url
9790                      * @param {String/Object} params
9791                      */
9792                     BEFOREUPDATE,
9793                     /**
9794                      * @event update
9795                      * Fired after successful update is made.
9796                      * @param {Ext.Element} el
9797                      * @param {Object} oResponseObject The response Object
9798                      */
9799                     UPDATE,
9800                     /**
9801                      * @event failure
9802                      * Fired on update failure.
9803                      * @param {Ext.Element} el
9804                      * @param {Object} oResponseObject The response Object
9805                      */
9806                     FAILURE
9807                 );
9808         
9809                 Ext.apply(me, Ext.Updater.defaults);
9810                 /**
9811                  * Blank page URL to use with SSL file uploads (defaults to {@link Ext.Updater.defaults#sslBlankUrl}).
9812                  * @property sslBlankUrl
9813                  * @type String
9814                  */
9815                 /**
9816                  * Whether to append unique parameter on get request to disable caching (defaults to {@link Ext.Updater.defaults#disableCaching}).
9817                  * @property disableCaching
9818                  * @type Boolean
9819                  */
9820                 /**
9821                  * Text for loading indicator (defaults to {@link Ext.Updater.defaults#indicatorText}).
9822                  * @property indicatorText
9823                  * @type String
9824                  */
9825                 /**
9826                  * Whether to show indicatorText when loading (defaults to {@link Ext.Updater.defaults#showLoadIndicator}).
9827                  * @property showLoadIndicator
9828                  * @type String
9829                  */
9830                 /**
9831                  * Timeout for requests or form posts in seconds (defaults to {@link Ext.Updater.defaults#timeout}).
9832                  * @property timeout
9833                  * @type Number
9834                  */
9835                 /**
9836                  * True to process scripts in the output (defaults to {@link Ext.Updater.defaults#loadScripts}).
9837                  * @property loadScripts
9838                  * @type Boolean
9839                  */
9840         
9841                 /**
9842                  * Transaction object of the current executing transaction, or null if there is no active transaction.
9843                  */
9844                 me.transaction = null;
9845                 /**
9846                  * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
9847                  * @type Function
9848                  */
9849                 me.refreshDelegate = me.refresh.createDelegate(me);
9850                 /**
9851                  * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
9852                  * @type Function
9853                  */
9854                 me.updateDelegate = me.update.createDelegate(me);
9855                 /**
9856                  * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
9857                  * @type Function
9858                  */
9859                 me.formUpdateDelegate = (me.formUpdate || function(){}).createDelegate(me);     
9860                 
9861                         /**
9862                          * The renderer for this Updater (defaults to {@link Ext.Updater.BasicRenderer}).
9863                          */
9864                 me.renderer = me.renderer || me.getDefaultRenderer();
9865                 
9866                 Ext.Updater.superclass.constructor.call(me);
9867             },
9868         
9869                 /**
9870              * Sets the content renderer for this Updater. See {@link Ext.Updater.BasicRenderer#render} for more details.
9871              * @param {Object} renderer The object implementing the render() method
9872              */
9873             setRenderer : function(renderer){
9874                 this.renderer = renderer;
9875             },  
9876         
9877             /**
9878              * Returns the current content renderer for this Updater. See {@link Ext.Updater.BasicRenderer#render} for more details.
9879              * @return {Object}
9880              */
9881             getRenderer : function(){
9882                return this.renderer;
9883             },
9884
9885             /**
9886              * This is an overrideable method which returns a reference to a default
9887              * renderer class if none is specified when creating the Ext.Updater.
9888              * Defaults to {@link Ext.Updater.BasicRenderer}
9889              */
9890             getDefaultRenderer: function() {
9891                 return new Ext.Updater.BasicRenderer();
9892             },
9893                 
9894             /**
9895              * Sets the default URL used for updates.
9896              * @param {String/Function} defaultUrl The url or a function to call to get the url
9897              */
9898             setDefaultUrl : function(defaultUrl){
9899                 this.defaultUrl = defaultUrl;
9900             },
9901         
9902             /**
9903              * Get the Element this Updater is bound to
9904              * @return {Ext.Element} The element
9905              */
9906             getEl : function(){
9907                 return this.el;
9908             },
9909         
9910                 /**
9911              * Performs an <b>asynchronous</b> request, updating this element with the response.
9912              * If params are specified it uses POST, otherwise it uses GET.<br><br>
9913              * <b>Note:</b> Due to the asynchronous nature of remote server requests, the Element
9914              * will not have been fully updated when the function returns. To post-process the returned
9915              * data, use the callback option, or an <b><code>update</code></b> event handler.
9916              * @param {Object} options A config object containing any of the following options:<ul>
9917              * <li>url : <b>String/Function</b><p class="sub-desc">The URL to request or a function which
9918              * <i>returns</i> the URL (defaults to the value of {@link Ext.Ajax#url} if not specified).</p></li>
9919              * <li>method : <b>String</b><p class="sub-desc">The HTTP method to
9920              * use. Defaults to POST if the <code>params</code> argument is present, otherwise GET.</p></li>
9921              * <li>params : <b>String/Object/Function</b><p class="sub-desc">The
9922              * parameters to pass to the server (defaults to none). These may be specified as a url-encoded
9923              * string, or as an object containing properties which represent parameters,
9924              * or as a function, which returns such an object.</p></li>
9925              * <li>scripts : <b>Boolean</b><p class="sub-desc">If <code>true</code>
9926              * any &lt;script&gt; tags embedded in the response text will be extracted
9927              * and executed (defaults to {@link Ext.Updater.defaults#loadScripts}). If this option is specified,
9928              * the callback will be called <i>after</i> the execution of the scripts.</p></li>
9929              * <li>callback : <b>Function</b><p class="sub-desc">A function to
9930              * be called when the response from the server arrives. The following
9931              * parameters are passed:<ul>
9932              * <li><b>el</b> : Ext.Element<p class="sub-desc">The Element being updated.</p></li>
9933              * <li><b>success</b> : Boolean<p class="sub-desc">True for success, false for failure.</p></li>
9934              * <li><b>response</b> : XMLHttpRequest<p class="sub-desc">The XMLHttpRequest which processed the update.</p></li>
9935              * <li><b>options</b> : Object<p class="sub-desc">The config object passed to the update call.</p></li></ul>
9936              * </p></li>
9937              * <li>scope : <b>Object</b><p class="sub-desc">The scope in which
9938              * to execute the callback (The callback's <code>this</code> reference.) If the
9939              * <code>params</code> argument is a function, this scope is used for that function also.</p></li>
9940              * <li>discardUrl : <b>Boolean</b><p class="sub-desc">By default, the URL of this request becomes
9941              * the default URL for this Updater object, and will be subsequently used in {@link #refresh}
9942              * calls.  To bypass this behavior, pass <code>discardUrl:true</code> (defaults to false).</p></li>
9943              * <li>timeout : <b>Number</b><p class="sub-desc">The number of seconds to wait for a response before
9944              * timing out (defaults to {@link Ext.Updater.defaults#timeout}).</p></li>
9945              * <li>text : <b>String</b><p class="sub-desc">The text to use as the innerHTML of the
9946              * {@link Ext.Updater.defaults#indicatorText} div (defaults to 'Loading...').  To replace the entire div, not
9947              * just the text, override {@link Ext.Updater.defaults#indicatorText} directly.</p></li>
9948              * <li>nocache : <b>Boolean</b><p class="sub-desc">Only needed for GET
9949              * requests, this option causes an extra, auto-generated parameter to be appended to the request
9950              * to defeat caching (defaults to {@link Ext.Updater.defaults#disableCaching}).</p></li></ul>
9951              * <p>
9952              * For example:
9953         <pre><code>
9954         um.update({
9955             url: "your-url.php",
9956             params: {param1: "foo", param2: "bar"}, // or a URL encoded string
9957             callback: yourFunction,
9958             scope: yourObject, //(optional scope)
9959             discardUrl: true,
9960             nocache: true,
9961             text: "Loading...",
9962             timeout: 60,
9963             scripts: false // Save time by avoiding RegExp execution.
9964         });
9965         </code></pre>
9966              */
9967             update : function(url, params, callback, discardUrl){
9968                     var me = this,
9969                         cfg, 
9970                         callerScope;
9971                         
9972                 if(me.fireEvent(BEFOREUPDATE, me.el, url, params) !== false){               
9973                     if(Ext.isObject(url)){ // must be config object
9974                         cfg = url;
9975                         url = cfg.url;
9976                         params = params || cfg.params;
9977                         callback = callback || cfg.callback;
9978                         discardUrl = discardUrl || cfg.discardUrl;
9979                         callerScope = cfg.scope;                        
9980                         if(!Ext.isEmpty(cfg.nocache)){me.disableCaching = cfg.nocache;};
9981                         if(!Ext.isEmpty(cfg.text)){me.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
9982                         if(!Ext.isEmpty(cfg.scripts)){me.loadScripts = cfg.scripts;};
9983                         if(!Ext.isEmpty(cfg.timeout)){me.timeout = cfg.timeout;};
9984                     }
9985                     me.showLoading();
9986         
9987                     if(!discardUrl){
9988                         me.defaultUrl = url;
9989                     }
9990                     if(Ext.isFunction(url)){
9991                         url = url.call(me);
9992                     }
9993         
9994                     var o = Ext.apply({}, {
9995                         url : url,
9996                         params: (Ext.isFunction(params) && callerScope) ? params.createDelegate(callerScope) : params,
9997                         success: processSuccess,
9998                         failure: processFailure,
9999                         scope: me,
10000                         callback: undefined,
10001                         timeout: (me.timeout*1000),
10002                         disableCaching: me.disableCaching,
10003                         argument: {
10004                             "options": cfg,
10005                             "url": url,
10006                             "form": null,
10007                             "callback": callback,
10008                             "scope": callerScope || window,
10009                             "params": params
10010                         }
10011                     }, cfg);
10012         
10013                     me.transaction = Ext.Ajax.request(o);
10014                 }
10015             },          
10016
10017                 /**
10018              * <p>Performs an asynchronous form post, updating this element with the response. If the form has the attribute
10019              * enctype="<a href="http://www.faqs.org/rfcs/rfc2388.html">multipart/form-data</a>", it assumes it's a file upload.
10020              * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.</p>
10021              * <p>File uploads are not performed using normal "Ajax" techniques, that is they are <b>not</b>
10022              * performed using XMLHttpRequests. Instead the form is submitted in the standard manner with the
10023              * DOM <code>&lt;form></code> element temporarily modified to have its
10024              * <a href="http://www.w3.org/TR/REC-html40/present/frames.html#adef-target">target</a> set to refer
10025              * to a dynamically generated, hidden <code>&lt;iframe></code> which is inserted into the document
10026              * but removed after the return data has been gathered.</p>
10027              * <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>
10028              * and some server technologies (notably JEE) may require some custom processing in order to
10029              * retrieve parameter names and parameter values from the packet content.</p>
10030              * @param {String/HTMLElement} form The form Id or form element
10031              * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
10032              * @param {Boolean} reset (optional) Whether to try to reset the form after the update
10033              * @param {Function} callback (optional) Callback when transaction is complete. The following
10034              * parameters are passed:<ul>
10035              * <li><b>el</b> : Ext.Element<p class="sub-desc">The Element being updated.</p></li>
10036              * <li><b>success</b> : Boolean<p class="sub-desc">True for success, false for failure.</p></li>
10037              * <li><b>response</b> : XMLHttpRequest<p class="sub-desc">The XMLHttpRequest which processed the update.</p></li></ul>
10038              */
10039             formUpdate : function(form, url, reset, callback){
10040                     var me = this;
10041                 if(me.fireEvent(BEFOREUPDATE, me.el, form, url) !== false){
10042                     if(Ext.isFunction(url)){
10043                         url = url.call(me);
10044                     }
10045                     form = Ext.getDom(form)
10046                     me.transaction = Ext.Ajax.request({
10047                         form: form,
10048                         url:url,
10049                         success: processSuccess,
10050                         failure: processFailure,
10051                         scope: me,
10052                         timeout: (me.timeout*1000),
10053                         argument: {
10054                             "url": url,
10055                             "form": form,
10056                             "callback": callback,
10057                             "reset": reset
10058                         }
10059                     });
10060                     me.showLoading.defer(1, me);
10061                 }
10062             },
10063                         
10064             /**
10065              * Set this element to auto refresh.  Can be canceled by calling {@link #stopAutoRefresh}.
10066              * @param {Number} interval How often to update (in seconds).
10067              * @param {String/Object/Function} url (optional) The url for this request, a config object in the same format
10068              * supported by {@link #load}, or a function to call to get the url (defaults to the last used url).  Note that while
10069              * the url used in a load call can be reused by this method, other load config options will not be reused and must be
10070              * sepcified as part of a config object passed as this paramter if needed.
10071              * @param {String/Object} params (optional) The parameters to pass as either a url encoded string
10072              * "&param1=1&param2=2" or as an object {param1: 1, param2: 2}
10073              * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
10074              * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
10075              */
10076             startAutoRefresh : function(interval, url, params, callback, refreshNow){
10077                     var me = this;
10078                 if(refreshNow){
10079                     me.update(url || me.defaultUrl, params, callback, true);
10080                 }
10081                 if(me.autoRefreshProcId){
10082                     clearInterval(me.autoRefreshProcId);
10083                 }
10084                 me.autoRefreshProcId = setInterval(me.update.createDelegate(me, [url || me.defaultUrl, params, callback, true]), interval * 1000);
10085             },
10086         
10087             /**
10088              * Stop auto refresh on this element.
10089              */
10090             stopAutoRefresh : function(){
10091                 if(this.autoRefreshProcId){
10092                     clearInterval(this.autoRefreshProcId);
10093                     delete this.autoRefreshProcId;
10094                 }
10095             },
10096         
10097             /**
10098              * Returns true if the Updater is currently set to auto refresh its content (see {@link #startAutoRefresh}), otherwise false.
10099              */
10100             isAutoRefreshing : function(){
10101                return !!this.autoRefreshProcId;
10102             },
10103         
10104             /**
10105              * Display the element's "loading" state. By default, the element is updated with {@link #indicatorText}. This
10106              * method may be overridden to perform a custom action while this Updater is actively updating its contents.
10107              */
10108             showLoading : function(){
10109                 if(this.showLoadIndicator){
10110                 this.el.dom.innerHTML = this.indicatorText;
10111                 }
10112             },
10113         
10114             /**
10115              * Aborts the currently executing transaction, if any.
10116              */
10117             abort : function(){
10118                 if(this.transaction){
10119                     Ext.Ajax.abort(this.transaction);
10120                 }
10121             },
10122         
10123             /**
10124              * Returns true if an update is in progress, otherwise false.
10125              * @return {Boolean}
10126              */
10127             isUpdating : function(){        
10128                 return this.transaction ? Ext.Ajax.isLoading(this.transaction) : false;        
10129             },
10130             
10131             /**
10132              * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
10133              * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
10134              */
10135             refresh : function(callback){
10136                 if(this.defaultUrl){
10137                         this.update(this.defaultUrl, null, callback, true);
10138                 }
10139             }
10140     }
10141 }());
10142
10143 /**
10144  * @class Ext.Updater.defaults
10145  * The defaults collection enables customizing the default properties of Updater
10146  */
10147 Ext.Updater.defaults = {
10148    /**
10149      * Timeout for requests or form posts in seconds (defaults to 30 seconds).
10150      * @type Number
10151      */
10152     timeout : 30,    
10153     /**
10154      * True to append a unique parameter to GET requests to disable caching (defaults to false).
10155      * @type Boolean
10156      */
10157     disableCaching : false,
10158     /**
10159      * Whether or not to show {@link #indicatorText} during loading (defaults to true).
10160      * @type Boolean
10161      */
10162     showLoadIndicator : true,
10163     /**
10164      * Text for loading indicator (defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
10165      * @type String
10166      */
10167     indicatorText : '<div class="loading-indicator">Loading...</div>',
10168      /**
10169      * True to process scripts by default (defaults to false).
10170      * @type Boolean
10171      */
10172     loadScripts : false,
10173     /**
10174     * Blank page URL to use with SSL file uploads (defaults to {@link Ext#SSL_SECURE_URL} if set, or "javascript:false").
10175     * @type String
10176     */
10177     sslBlankUrl : Ext.SSL_SECURE_URL      
10178 };
10179
10180
10181 /**
10182  * Static convenience method. <b>This method is deprecated in favor of el.load({url:'foo.php', ...})</b>.
10183  * Usage:
10184  * <pre><code>Ext.Updater.updateElement("my-div", "stuff.php");</code></pre>
10185  * @param {Mixed} el The element to update
10186  * @param {String} url The url
10187  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
10188  * @param {Object} options (optional) A config object with any of the Updater properties you want to set - for
10189  * example: {disableCaching:true, indicatorText: "Loading data..."}
10190  * @static
10191  * @deprecated
10192  * @member Ext.Updater
10193  */
10194 Ext.Updater.updateElement = function(el, url, params, options){
10195     var um = Ext.get(el).getUpdater();
10196     Ext.apply(um, options);
10197     um.update(url, params, options ? options.callback : null);
10198 };
10199
10200 /**
10201  * @class Ext.Updater.BasicRenderer
10202  * <p>This class is a base class implementing a simple render method which updates an element using results from an Ajax request.</p>
10203  * <p>The BasicRenderer updates the element's innerHTML with the responseText. To perform a custom render (i.e. XML or JSON processing),
10204  * create an object with a conforming {@link #render} method and pass it to setRenderer on the Updater.</p>
10205  */
10206 Ext.Updater.BasicRenderer = function(){};
10207
10208 Ext.Updater.BasicRenderer.prototype = {
10209     /**
10210      * This method is called when an Ajax response is received, and an Element needs updating.
10211      * @param {Ext.Element} el The element being rendered
10212      * @param {Object} xhr The XMLHttpRequest object
10213      * @param {Updater} updateManager The calling update manager
10214      * @param {Function} callback A callback that will need to be called if loadScripts is true on the Updater
10215      */
10216      render : function(el, response, updateManager, callback){       
10217         el.update(response.responseText, updateManager.loadScripts, callback);
10218     }
10219 };/**
10220  * @class Date
10221  *
10222  * The date parsing and formatting syntax contains a subset of
10223  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
10224  * supported will provide results equivalent to their PHP versions.
10225  *
10226  * The following is a list of all currently supported formats:
10227  * <pre>
10228 Format  Description                                                               Example returned values
10229 ------  -----------------------------------------------------------------------   -----------------------
10230   d     Day of the month, 2 digits with leading zeros                             01 to 31
10231   D     A short textual representation of the day of the week                     Mon to Sun
10232   j     Day of the month without leading zeros                                    1 to 31
10233   l     A full textual representation of the day of the week                      Sunday to Saturday
10234   N     ISO-8601 numeric representation of the day of the week                    1 (for Monday) through 7 (for Sunday)
10235   S     English ordinal suffix for the day of the month, 2 characters             st, nd, rd or th. Works well with j
10236   w     Numeric representation of the day of the week                             0 (for Sunday) to 6 (for Saturday)
10237   z     The day of the year (starting from 0)                                     0 to 364 (365 in leap years)
10238   W     ISO-8601 week number of year, weeks starting on Monday                    01 to 53
10239   F     A full textual representation of a month, such as January or March        January to December
10240   m     Numeric representation of a month, with leading zeros                     01 to 12
10241   M     A short textual representation of a month                                 Jan to Dec
10242   n     Numeric representation of a month, without leading zeros                  1 to 12
10243   t     Number of days in the given month                                         28 to 31
10244   L     Whether it's a leap year                                                  1 if it is a leap year, 0 otherwise.
10245   o     ISO-8601 year number (identical to (Y), but if the ISO week number (W)    Examples: 1998 or 2004
10246         belongs to the previous or next year, that year is used instead)
10247   Y     A full numeric representation of a year, 4 digits                         Examples: 1999 or 2003
10248   y     A two digit representation of a year                                      Examples: 99 or 03
10249   a     Lowercase Ante meridiem and Post meridiem                                 am or pm
10250   A     Uppercase Ante meridiem and Post meridiem                                 AM or PM
10251   g     12-hour format of an hour without leading zeros                           1 to 12
10252   G     24-hour format of an hour without leading zeros                           0 to 23
10253   h     12-hour format of an hour with leading zeros                              01 to 12
10254   H     24-hour format of an hour with leading zeros                              00 to 23
10255   i     Minutes, with leading zeros                                               00 to 59
10256   s     Seconds, with leading zeros                                               00 to 59
10257   u     Decimal fraction of a second                                              Examples:
10258         (minimum 1 digit, arbitrary number of digits allowed)                     001 (i.e. 0.001s) or
10259                                                                                   100 (i.e. 0.100s) or
10260                                                                                   999 (i.e. 0.999s) or
10261                                                                                   999876543210 (i.e. 0.999876543210s)
10262   O     Difference to Greenwich time (GMT) in hours and minutes                   Example: +1030
10263   P     Difference to Greenwich time (GMT) with colon between hours and minutes   Example: -08:00
10264   T     Timezone abbreviation of the machine running the code                     Examples: EST, MDT, PDT ...
10265   Z     Timezone offset in seconds (negative if west of UTC, positive if east)    -43200 to 50400
10266   c     ISO 8601 date
10267         Notes:                                                                    Examples:
10268         1) If unspecified, the month / day defaults to the current month / day,   1991 or
10269            the time defaults to midnight, while the timezone defaults to the      1992-10 or
10270            browser's timezone. If a time is specified, it must include both hours 1993-09-20 or
10271            and minutes. The "T" delimiter, seconds, milliseconds and timezone     1994-08-19T16:20+01:00 or
10272            are optional.                                                          1995-07-18T17:21:28-02:00 or
10273         2) The decimal fraction of a second, if specified, must contain at        1996-06-17T18:22:29.98765+03:00 or
10274            least 1 digit (there is no limit to the maximum number                 1997-05-16T19:23:30,12345-0400 or
10275            of digits allowed), and may be delimited by either a '.' or a ','      1998-04-15T20:24:31.2468Z or
10276         Refer to the examples on the right for the various levels of              1999-03-14T20:24:32Z or
10277         date-time granularity which are supported, or see                         2000-02-13T21:25:33
10278         http://www.w3.org/TR/NOTE-datetime for more info.                         2001-01-12 22:26:34
10279   U     Seconds since the Unix Epoch (January 1 1970 00:00:00 GMT)                1193432466 or -2138434463
10280   M$    Microsoft AJAX serialized dates                                           \/Date(1238606590509)\/ (i.e. UTC milliseconds since epoch) or
10281                                                                                   \/Date(1238606590509+0800)\/
10282 </pre>
10283  *
10284  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
10285  * <pre><code>
10286 // Sample date:
10287 // 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
10288
10289 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
10290 document.write(dt.format('Y-m-d'));                           // 2007-01-10
10291 document.write(dt.format('F j, Y, g:i a'));                   // January 10, 2007, 3:05 pm
10292 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
10293 </code></pre>
10294  *
10295  * Here are some standard date/time patterns that you might find helpful.  They
10296  * are not part of the source of Date.js, but to use them you can simply copy this
10297  * block of code into any script that is included after Date.js and they will also become
10298  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
10299  * <pre><code>
10300 Date.patterns = {
10301     ISO8601Long:"Y-m-d H:i:s",
10302     ISO8601Short:"Y-m-d",
10303     ShortDate: "n/j/Y",
10304     LongDate: "l, F d, Y",
10305     FullDateTime: "l, F d, Y g:i:s A",
10306     MonthDay: "F d",
10307     ShortTime: "g:i A",
10308     LongTime: "g:i:s A",
10309     SortableDateTime: "Y-m-d\\TH:i:s",
10310     UniversalSortableDateTime: "Y-m-d H:i:sO",
10311     YearMonth: "F, Y"
10312 };
10313 </code></pre>
10314  *
10315  * Example usage:
10316  * <pre><code>
10317 var dt = new Date();
10318 document.write(dt.format(Date.patterns.ShortDate));
10319 </code></pre>
10320  * <p>Developer-written, custom formats may be used by supplying both a formatting and a parsing function
10321  * which perform to specialized requirements. The functions are stored in {@link #parseFunctions} and {@link #formatFunctions}.</p>
10322  */
10323
10324 /*
10325  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
10326  * (see http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/)
10327  * They generate precompiled functions from format patterns instead of parsing and
10328  * processing each pattern every time a date is formatted. These functions are available
10329  * on every Date object.
10330  */
10331
10332 (function() {
10333
10334 /**
10335  * Global flag which determines if strict date parsing should be used.
10336  * Strict date parsing will not roll-over invalid dates, which is the
10337  * default behaviour of javascript Date objects.
10338  * (see {@link #parseDate} for more information)
10339  * Defaults to <tt>false</tt>.
10340  * @static
10341  * @type Boolean
10342 */
10343 Date.useStrict = false;
10344
10345
10346 // create private copy of Ext's String.format() method
10347 // - to remove unnecessary dependency
10348 // - to resolve namespace conflict with M$-Ajax's implementation
10349 function xf(format) {
10350     var args = Array.prototype.slice.call(arguments, 1);
10351     return format.replace(/\{(\d+)\}/g, function(m, i) {
10352         return args[i];
10353     });
10354 }
10355
10356
10357 // private
10358 Date.formatCodeToRegex = function(character, currentGroup) {
10359     // Note: currentGroup - position in regex result array (see notes for Date.parseCodes below)
10360     var p = Date.parseCodes[character];
10361
10362     if (p) {
10363       p = typeof p == 'function'? p() : p;
10364       Date.parseCodes[character] = p; // reassign function result to prevent repeated execution
10365     }
10366
10367     return p? Ext.applyIf({
10368       c: p.c? xf(p.c, currentGroup || "{0}") : p.c
10369     }, p) : {
10370         g:0,
10371         c:null,
10372         s:Ext.escapeRe(character) // treat unrecognised characters as literals
10373     }
10374 }
10375
10376 // private shorthand for Date.formatCodeToRegex since we'll be using it fairly often
10377 var $f = Date.formatCodeToRegex;
10378
10379 Ext.apply(Date, {
10380     /**
10381      * <p>An object hash in which each property is a date parsing function. The property name is the
10382      * format string which that function parses.</p>
10383      * <p>This object is automatically populated with date parsing functions as
10384      * date formats are requested for Ext standard formatting strings.</p>
10385      * <p>Custom parsing functions may be inserted into this object, keyed by a name which from then on
10386      * may be used as a format string to {@link #parseDate}.<p>
10387      * <p>Example:</p><pre><code>
10388 Date.parseFunctions['x-date-format'] = myDateParser;
10389 </code></pre>
10390      * <p>A parsing function should return a Date object, and is passed the following parameters:<div class="mdetail-params"><ul>
10391      * <li><code>date</code> : String<div class="sub-desc">The date string to parse.</div></li>
10392      * <li><code>strict</code> : Boolean<div class="sub-desc">True to validate date strings while parsing
10393      * (i.e. prevent javascript Date "rollover") (The default must be false).
10394      * Invalid date strings should return null when parsed.</div></li>
10395      * </ul></div></p>
10396      * <p>To enable Dates to also be <i>formatted</i> according to that format, a corresponding
10397      * formatting function must be placed into the {@link #formatFunctions} property.
10398      * @property parseFunctions
10399      * @static
10400      * @type Object
10401      */
10402     parseFunctions: {
10403         "M$": function(input, strict) {
10404             // note: the timezone offset is ignored since the M$ Ajax server sends
10405             // a UTC milliseconds-since-Unix-epoch value (negative values are allowed)
10406             var re = new RegExp('\\/Date\\(([-+])?(\\d+)(?:[+-]\\d{4})?\\)\\/');
10407             var r = (input || '').match(re);
10408             return r? new Date(((r[1] || '') + r[2]) * 1) : null;
10409         }
10410     },
10411     parseRegexes: [],
10412
10413     /**
10414      * <p>An object hash in which each property is a date formatting function. The property name is the
10415      * format string which corresponds to the produced formatted date string.</p>
10416      * <p>This object is automatically populated with date formatting functions as
10417      * date formats are requested for Ext standard formatting strings.</p>
10418      * <p>Custom formatting functions may be inserted into this object, keyed by a name which from then on
10419      * may be used as a format string to {@link #format}. Example:</p><pre><code>
10420 Date.formatFunctions['x-date-format'] = myDateFormatter;
10421 </code></pre>
10422      * <p>A formatting function should return a string repesentation of the passed Date object:<div class="mdetail-params"><ul>
10423      * <li><code>date</code> : Date<div class="sub-desc">The Date to format.</div></li>
10424      * </ul></div></p>
10425      * <p>To enable date strings to also be <i>parsed</i> according to that format, a corresponding
10426      * parsing function must be placed into the {@link #parseFunctions} property.
10427      * @property formatFunctions
10428      * @static
10429      * @type Object
10430      */
10431     formatFunctions: {
10432         "M$": function() {
10433             // UTC milliseconds since Unix epoch (M$-AJAX serialized date format (MRSF))
10434             return '\\/Date(' + this.getTime() + ')\\/';
10435         }
10436     },
10437
10438     y2kYear : 50,
10439
10440     /**
10441      * Date interval constant
10442      * @static
10443      * @type String
10444      */
10445     MILLI : "ms",
10446
10447     /**
10448      * Date interval constant
10449      * @static
10450      * @type String
10451      */
10452     SECOND : "s",
10453
10454     /**
10455      * Date interval constant
10456      * @static
10457      * @type String
10458      */
10459     MINUTE : "mi",
10460
10461     /** Date interval constant
10462      * @static
10463      * @type String
10464      */
10465     HOUR : "h",
10466
10467     /**
10468      * Date interval constant
10469      * @static
10470      * @type String
10471      */
10472     DAY : "d",
10473
10474     /**
10475      * Date interval constant
10476      * @static
10477      * @type String
10478      */
10479     MONTH : "mo",
10480
10481     /**
10482      * Date interval constant
10483      * @static
10484      * @type String
10485      */
10486     YEAR : "y",
10487
10488     /**
10489      * <p>An object hash containing default date values used during date parsing.</p>
10490      * <p>The following properties are available:<div class="mdetail-params"><ul>
10491      * <li><code>y</code> : Number<div class="sub-desc">The default year value. (defaults to undefined)</div></li>
10492      * <li><code>m</code> : Number<div class="sub-desc">The default 1-based month value. (defaults to undefined)</div></li>
10493      * <li><code>d</code> : Number<div class="sub-desc">The default day value. (defaults to undefined)</div></li>
10494      * <li><code>h</code> : Number<div class="sub-desc">The default hour value. (defaults to undefined)</div></li>
10495      * <li><code>i</code> : Number<div class="sub-desc">The default minute value. (defaults to undefined)</div></li>
10496      * <li><code>s</code> : Number<div class="sub-desc">The default second value. (defaults to undefined)</div></li>
10497      * <li><code>ms</code> : Number<div class="sub-desc">The default millisecond value. (defaults to undefined)</div></li>
10498      * </ul></div></p>
10499      * <p>Override these properties to customize the default date values used by the {@link #parseDate} method.</p>
10500      * <p><b>Note: In countries which experience Daylight Saving Time (i.e. DST), the <tt>h</tt>, <tt>i</tt>, <tt>s</tt>
10501      * and <tt>ms</tt> properties may coincide with the exact time in which DST takes effect.
10502      * It is the responsiblity of the developer to account for this.</b></p>
10503      * Example Usage:
10504      * <pre><code>
10505 // set default day value to the first day of the month
10506 Date.defaults.d = 1;
10507
10508 // parse a February date string containing only year and month values.
10509 // setting the default day value to 1 prevents weird date rollover issues
10510 // when attempting to parse the following date string on, for example, March 31st 2009.
10511 Date.parseDate('2009-02', 'Y-m'); // returns a Date object representing February 1st 2009
10512 </code></pre>
10513      * @property defaults
10514      * @static
10515      * @type Object
10516      */
10517     defaults: {},
10518
10519     /**
10520      * An array of textual day names.
10521      * Override these values for international dates.
10522      * Example:
10523      * <pre><code>
10524 Date.dayNames = [
10525     'SundayInYourLang',
10526     'MondayInYourLang',
10527     ...
10528 ];
10529 </code></pre>
10530      * @type Array
10531      * @static
10532      */
10533     dayNames : [
10534         "Sunday",
10535         "Monday",
10536         "Tuesday",
10537         "Wednesday",
10538         "Thursday",
10539         "Friday",
10540         "Saturday"
10541     ],
10542
10543     /**
10544      * An array of textual month names.
10545      * Override these values for international dates.
10546      * Example:
10547      * <pre><code>
10548 Date.monthNames = [
10549     'JanInYourLang',
10550     'FebInYourLang',
10551     ...
10552 ];
10553 </code></pre>
10554      * @type Array
10555      * @static
10556      */
10557     monthNames : [
10558         "January",
10559         "February",
10560         "March",
10561         "April",
10562         "May",
10563         "June",
10564         "July",
10565         "August",
10566         "September",
10567         "October",
10568         "November",
10569         "December"
10570     ],
10571
10572     /**
10573      * An object hash of zero-based javascript month numbers (with short month names as keys. note: keys are case-sensitive).
10574      * Override these values for international dates.
10575      * Example:
10576      * <pre><code>
10577 Date.monthNumbers = {
10578     'ShortJanNameInYourLang':0,
10579     'ShortFebNameInYourLang':1,
10580     ...
10581 };
10582 </code></pre>
10583      * @type Object
10584      * @static
10585      */
10586     monthNumbers : {
10587         Jan:0,
10588         Feb:1,
10589         Mar:2,
10590         Apr:3,
10591         May:4,
10592         Jun:5,
10593         Jul:6,
10594         Aug:7,
10595         Sep:8,
10596         Oct:9,
10597         Nov:10,
10598         Dec:11
10599     },
10600
10601     /**
10602      * Get the short month name for the given month number.
10603      * Override this function for international dates.
10604      * @param {Number} month A zero-based javascript month number.
10605      * @return {String} The short month name.
10606      * @static
10607      */
10608     getShortMonthName : function(month) {
10609         return Date.monthNames[month].substring(0, 3);
10610     },
10611
10612     /**
10613      * Get the short day name for the given day number.
10614      * Override this function for international dates.
10615      * @param {Number} day A zero-based javascript day number.
10616      * @return {String} The short day name.
10617      * @static
10618      */
10619     getShortDayName : function(day) {
10620         return Date.dayNames[day].substring(0, 3);
10621     },
10622
10623     /**
10624      * Get the zero-based javascript month number for the given short/full month name.
10625      * Override this function for international dates.
10626      * @param {String} name The short/full month name.
10627      * @return {Number} The zero-based javascript month number.
10628      * @static
10629      */
10630     getMonthNumber : function(name) {
10631         // handle camel casing for english month names (since the keys for the Date.monthNumbers hash are case sensitive)
10632         return Date.monthNumbers[name.substring(0, 1).toUpperCase() + name.substring(1, 3).toLowerCase()];
10633     },
10634
10635     /**
10636      * The base format-code to formatting-function hashmap used by the {@link #format} method.
10637      * Formatting functions are strings (or functions which return strings) which
10638      * will return the appropriate value when evaluated in the context of the Date object
10639      * from which the {@link #format} method is called.
10640      * Add to / override these mappings for custom date formatting.
10641      * Note: Date.format() treats characters as literals if an appropriate mapping cannot be found.
10642      * Example:
10643      * <pre><code>
10644 Date.formatCodes.x = "String.leftPad(this.getDate(), 2, '0')";
10645 (new Date()).format("X"); // returns the current day of the month
10646 </code></pre>
10647      * @type Object
10648      * @static
10649      */
10650     formatCodes : {
10651         d: "String.leftPad(this.getDate(), 2, '0')",
10652         D: "Date.getShortDayName(this.getDay())", // get localised short day name
10653         j: "this.getDate()",
10654         l: "Date.dayNames[this.getDay()]",
10655         N: "(this.getDay() ? this.getDay() : 7)",
10656         S: "this.getSuffix()",
10657         w: "this.getDay()",
10658         z: "this.getDayOfYear()",
10659         W: "String.leftPad(this.getWeekOfYear(), 2, '0')",
10660         F: "Date.monthNames[this.getMonth()]",
10661         m: "String.leftPad(this.getMonth() + 1, 2, '0')",
10662         M: "Date.getShortMonthName(this.getMonth())", // get localised short month name
10663         n: "(this.getMonth() + 1)",
10664         t: "this.getDaysInMonth()",
10665         L: "(this.isLeapYear() ? 1 : 0)",
10666         o: "(this.getFullYear() + (this.getWeekOfYear() == 1 && this.getMonth() > 0 ? +1 : (this.getWeekOfYear() >= 52 && this.getMonth() < 11 ? -1 : 0)))",
10667         Y: "this.getFullYear()",
10668         y: "('' + this.getFullYear()).substring(2, 4)",
10669         a: "(this.getHours() < 12 ? 'am' : 'pm')",
10670         A: "(this.getHours() < 12 ? 'AM' : 'PM')",
10671         g: "((this.getHours() % 12) ? this.getHours() % 12 : 12)",
10672         G: "this.getHours()",
10673         h: "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0')",
10674         H: "String.leftPad(this.getHours(), 2, '0')",
10675         i: "String.leftPad(this.getMinutes(), 2, '0')",
10676         s: "String.leftPad(this.getSeconds(), 2, '0')",
10677         u: "String.leftPad(this.getMilliseconds(), 3, '0')",
10678         O: "this.getGMTOffset()",
10679         P: "this.getGMTOffset(true)",
10680         T: "this.getTimezone()",
10681         Z: "(this.getTimezoneOffset() * -60)",
10682
10683         c: function() { // ISO-8601 -- GMT format
10684             for (var c = "Y-m-dTH:i:sP", code = [], i = 0, l = c.length; i < l; ++i) {
10685                 var e = c.charAt(i);
10686                 code.push(e == "T" ? "'T'" : Date.getFormatCode(e)); // treat T as a character literal
10687             }
10688             return code.join(" + ");
10689         },
10690         /*
10691         c: function() { // ISO-8601 -- UTC format
10692             return [
10693               "this.getUTCFullYear()", "'-'",
10694               "String.leftPad(this.getUTCMonth() + 1, 2, '0')", "'-'",
10695               "String.leftPad(this.getUTCDate(), 2, '0')",
10696               "'T'",
10697               "String.leftPad(this.getUTCHours(), 2, '0')", "':'",
10698               "String.leftPad(this.getUTCMinutes(), 2, '0')", "':'",
10699               "String.leftPad(this.getUTCSeconds(), 2, '0')",
10700               "'Z'"
10701             ].join(" + ");
10702         },
10703         */
10704
10705         U: "Math.round(this.getTime() / 1000)"
10706     },
10707
10708     /**
10709      * Checks if the passed Date parameters will cause a javascript Date "rollover".
10710      * @param {Number} year 4-digit year
10711      * @param {Number} month 1-based month-of-year
10712      * @param {Number} day Day of month
10713      * @param {Number} hour (optional) Hour
10714      * @param {Number} minute (optional) Minute
10715      * @param {Number} second (optional) Second
10716      * @param {Number} millisecond (optional) Millisecond
10717      * @return {Boolean} true if the passed parameters do not cause a Date "rollover", false otherwise.
10718      * @static
10719      */
10720     isValid : function(y, m, d, h, i, s, ms) {
10721         // setup defaults
10722         h = h || 0;
10723         i = i || 0;
10724         s = s || 0;
10725         ms = ms || 0;
10726
10727         var dt = new Date(y, m - 1, d, h, i, s, ms);
10728
10729         return y == dt.getFullYear() &&
10730             m == dt.getMonth() + 1 &&
10731             d == dt.getDate() &&
10732             h == dt.getHours() &&
10733             i == dt.getMinutes() &&
10734             s == dt.getSeconds() &&
10735             ms == dt.getMilliseconds();
10736     },
10737
10738     /**
10739      * Parses the passed string using the specified date format.
10740      * Note that this function expects normal calendar dates, meaning that months are 1-based (i.e. 1 = January).
10741      * The {@link #defaults} hash will be used for any date value (i.e. year, month, day, hour, minute, second or millisecond)
10742      * which cannot be found in the passed string. If a corresponding default date value has not been specified in the {@link #defaults} hash,
10743      * the current date's year, month, day or DST-adjusted zero-hour time value will be used instead.
10744      * Keep in mind that the input date string must precisely match the specified format string
10745      * in order for the parse operation to be successful (failed parse operations return a null value).
10746      * <p>Example:</p><pre><code>
10747 //dt = Fri May 25 2007 (current date)
10748 var dt = new Date();
10749
10750 //dt = Thu May 25 2006 (today&#39;s month/day in 2006)
10751 dt = Date.parseDate("2006", "Y");
10752
10753 //dt = Sun Jan 15 2006 (all date parts specified)
10754 dt = Date.parseDate("2006-01-15", "Y-m-d");
10755
10756 //dt = Sun Jan 15 2006 15:20:01
10757 dt = Date.parseDate("2006-01-15 3:20:01 PM", "Y-m-d g:i:s A");
10758
10759 // attempt to parse Sun Feb 29 2006 03:20:01 in strict mode
10760 dt = Date.parseDate("2006-02-29 03:20:01", "Y-m-d H:i:s", true); // returns null
10761 </code></pre>
10762      * @param {String} input The raw date string.
10763      * @param {String} format The expected date string format.
10764      * @param {Boolean} strict (optional) True to validate date strings while parsing (i.e. prevents javascript Date "rollover")
10765                         (defaults to false). Invalid date strings will return null when parsed.
10766      * @return {Date} The parsed Date.
10767      * @static
10768      */
10769     parseDate : function(input, format, strict) {
10770         var p = Date.parseFunctions;
10771         if (p[format] == null) {
10772             Date.createParser(format);
10773         }
10774         return p[format](input, Ext.isDefined(strict) ? strict : Date.useStrict);
10775     },
10776
10777     // private
10778     getFormatCode : function(character) {
10779         var f = Date.formatCodes[character];
10780
10781         if (f) {
10782           f = typeof f == 'function'? f() : f;
10783           Date.formatCodes[character] = f; // reassign function result to prevent repeated execution
10784         }
10785
10786         // note: unknown characters are treated as literals
10787         return f || ("'" + String.escape(character) + "'");
10788     },
10789
10790     // private
10791     createFormat : function(format) {
10792         var code = [],
10793             special = false,
10794             ch = '';
10795
10796         for (var i = 0; i < format.length; ++i) {
10797             ch = format.charAt(i);
10798             if (!special && ch == "\\") {
10799                 special = true;
10800             } else if (special) {
10801                 special = false;
10802                 code.push("'" + String.escape(ch) + "'");
10803             } else {
10804                 code.push(Date.getFormatCode(ch))
10805             }
10806         }
10807         Date.formatFunctions[format] = new Function("return " + code.join('+'));
10808     },
10809
10810     // private
10811     createParser : function() {
10812         var code = [
10813             "var dt, y, m, d, h, i, s, ms, o, z, zz, u, v,",
10814                 "def = Date.defaults,",
10815                 "results = String(input).match(Date.parseRegexes[{0}]);", // either null, or an array of matched strings
10816
10817             "if(results){",
10818                 "{1}",
10819
10820                 "if(u != null){", // i.e. unix time is defined
10821                     "v = new Date(u * 1000);", // give top priority to UNIX time
10822                 "}else{",
10823                     // create Date object representing midnight of the current day;
10824                     // this will provide us with our date defaults
10825                     // (note: clearTime() handles Daylight Saving Time automatically)
10826                     "dt = (new Date()).clearTime();",
10827
10828                     // date calculations (note: these calculations create a dependency on Ext.num())
10829                     "y = y >= 0? y : Ext.num(def.y, dt.getFullYear());",
10830                     "m = m >= 0? m : Ext.num(def.m - 1, dt.getMonth());",
10831                     "d = d >= 0? d : Ext.num(def.d, dt.getDate());",
10832
10833                     // time calculations (note: these calculations create a dependency on Ext.num())
10834                     "h  = h || Ext.num(def.h, dt.getHours());",
10835                     "i  = i || Ext.num(def.i, dt.getMinutes());",
10836                     "s  = s || Ext.num(def.s, dt.getSeconds());",
10837                     "ms = ms || Ext.num(def.ms, dt.getMilliseconds());",
10838
10839                     "if(z >= 0 && y >= 0){",
10840                         // both the year and zero-based day of year are defined and >= 0.
10841                         // these 2 values alone provide sufficient info to create a full date object
10842
10843                         // create Date object representing January 1st for the given year
10844                         "v = new Date(y, 0, 1, h, i, s, ms);",
10845
10846                         // then add day of year, checking for Date "rollover" if necessary
10847                         "v = !strict? v : (strict === true && (z <= 364 || (v.isLeapYear() && z <= 365))? v.add(Date.DAY, z) : null);",
10848                     "}else if(strict === true && !Date.isValid(y, m + 1, d, h, i, s, ms)){", // check for Date "rollover"
10849                         "v = null;", // invalid date, so return null
10850                     "}else{",
10851                         // plain old Date object
10852                         "v = new Date(y, m, d, h, i, s, ms);",
10853                     "}",
10854                 "}",
10855             "}",
10856
10857             "if(v){",
10858                 // favour UTC offset over GMT offset
10859                 "if(zz != null){",
10860                     // reset to UTC, then add offset
10861                     "v = v.add(Date.SECOND, -v.getTimezoneOffset() * 60 - zz);",
10862                 "}else if(o){",
10863                     // reset to GMT, then add offset
10864                     "v = v.add(Date.MINUTE, -v.getTimezoneOffset() + (sn == '+'? -1 : 1) * (hr * 60 + mn));",
10865                 "}",
10866             "}",
10867
10868             "return v;"
10869         ].join('\n');
10870
10871         return function(format) {
10872             var regexNum = Date.parseRegexes.length,
10873                 currentGroup = 1,
10874                 calc = [],
10875                 regex = [],
10876                 special = false,
10877                 ch = "";
10878
10879             for (var i = 0; i < format.length; ++i) {
10880                 ch = format.charAt(i);
10881                 if (!special && ch == "\\") {
10882                     special = true;
10883                 } else if (special) {
10884                     special = false;
10885                     regex.push(String.escape(ch));
10886                 } else {
10887                     var obj = $f(ch, currentGroup);
10888                     currentGroup += obj.g;
10889                     regex.push(obj.s);
10890                     if (obj.g && obj.c) {
10891                         calc.push(obj.c);
10892                     }
10893                 }
10894             }
10895
10896             Date.parseRegexes[regexNum] = new RegExp("^" + regex.join('') + "$", "i");
10897             Date.parseFunctions[format] = new Function("input", "strict", xf(code, regexNum, calc.join('')));
10898         }
10899     }(),
10900
10901     // private
10902     parseCodes : {
10903         /*
10904          * Notes:
10905          * g = {Number} calculation group (0 or 1. only group 1 contributes to date calculations.)
10906          * c = {String} calculation method (required for group 1. null for group 0. {0} = currentGroup - position in regex result array)
10907          * s = {String} regex pattern. all matches are stored in results[], and are accessible by the calculation mapped to 'c'
10908          */
10909         d: {
10910             g:1,
10911             c:"d = parseInt(results[{0}], 10);\n",
10912             s:"(\\d{2})" // day of month with leading zeroes (01 - 31)
10913         },
10914         j: {
10915             g:1,
10916             c:"d = parseInt(results[{0}], 10);\n",
10917             s:"(\\d{1,2})" // day of month without leading zeroes (1 - 31)
10918         },
10919         D: function() {
10920             for (var a = [], i = 0; i < 7; a.push(Date.getShortDayName(i)), ++i); // get localised short day names
10921             return {
10922                 g:0,
10923                 c:null,
10924                 s:"(?:" + a.join("|") +")"
10925             }
10926         },
10927         l: function() {
10928             return {
10929                 g:0,
10930                 c:null,
10931                 s:"(?:" + Date.dayNames.join("|") + ")"
10932             }
10933         },
10934         N: {
10935             g:0,
10936             c:null,
10937             s:"[1-7]" // ISO-8601 day number (1 (monday) - 7 (sunday))
10938         },
10939         S: {
10940             g:0,
10941             c:null,
10942             s:"(?:st|nd|rd|th)"
10943         },
10944         w: {
10945             g:0,
10946             c:null,
10947             s:"[0-6]" // javascript day number (0 (sunday) - 6 (saturday))
10948         },
10949         z: {
10950             g:1,
10951             c:"z = parseInt(results[{0}], 10);\n",
10952             s:"(\\d{1,3})" // day of the year (0 - 364 (365 in leap years))
10953         },
10954         W: {
10955             g:0,
10956             c:null,
10957             s:"(?:\\d{2})" // ISO-8601 week number (with leading zero)
10958         },
10959         F: function() {
10960             return {
10961                 g:1,
10962                 c:"m = parseInt(Date.getMonthNumber(results[{0}]), 10);\n", // get localised month number
10963                 s:"(" + Date.monthNames.join("|") + ")"
10964             }
10965         },
10966         M: function() {
10967             for (var a = [], i = 0; i < 12; a.push(Date.getShortMonthName(i)), ++i); // get localised short month names
10968             return Ext.applyIf({
10969                 s:"(" + a.join("|") + ")"
10970             }, $f("F"));
10971         },
10972         m: {
10973             g:1,
10974             c:"m = parseInt(results[{0}], 10) - 1;\n",
10975             s:"(\\d{2})" // month number with leading zeros (01 - 12)
10976         },
10977         n: {
10978             g:1,
10979             c:"m = parseInt(results[{0}], 10) - 1;\n",
10980             s:"(\\d{1,2})" // month number without leading zeros (1 - 12)
10981         },
10982         t: {
10983             g:0,
10984             c:null,
10985             s:"(?:\\d{2})" // no. of days in the month (28 - 31)
10986         },
10987         L: {
10988             g:0,
10989             c:null,
10990             s:"(?:1|0)"
10991         },
10992         o: function() {
10993             return $f("Y");
10994         },
10995         Y: {
10996             g:1,
10997             c:"y = parseInt(results[{0}], 10);\n",
10998             s:"(\\d{4})" // 4-digit year
10999         },
11000         y: {
11001             g:1,
11002             c:"var ty = parseInt(results[{0}], 10);\n"
11003                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n", // 2-digit year
11004             s:"(\\d{1,2})"
11005         },
11006         a: {
11007             g:1,
11008             c:"if (results[{0}] == 'am') {\n"
11009                 + "if (!h || h == 12) { h = 0; }\n"
11010                 + "} else { if (!h || h < 12) { h = (h || 0) + 12; }}",
11011             s:"(am|pm)"
11012         },
11013         A: {
11014             g:1,
11015             c:"if (results[{0}] == 'AM') {\n"
11016                 + "if (!h || h == 12) { h = 0; }\n"
11017                 + "} else { if (!h || h < 12) { h = (h || 0) + 12; }}",
11018             s:"(AM|PM)"
11019         },
11020         g: function() {
11021             return $f("G");
11022         },
11023         G: {
11024             g:1,
11025             c:"h = parseInt(results[{0}], 10);\n",
11026             s:"(\\d{1,2})" // 24-hr format of an hour without leading zeroes (0 - 23)
11027         },
11028         h: function() {
11029             return $f("H");
11030         },
11031         H: {
11032             g:1,
11033             c:"h = parseInt(results[{0}], 10);\n",
11034             s:"(\\d{2})" //  24-hr format of an hour with leading zeroes (00 - 23)
11035         },
11036         i: {
11037             g:1,
11038             c:"i = parseInt(results[{0}], 10);\n",
11039             s:"(\\d{2})" // minutes with leading zeros (00 - 59)
11040         },
11041         s: {
11042             g:1,
11043             c:"s = parseInt(results[{0}], 10);\n",
11044             s:"(\\d{2})" // seconds with leading zeros (00 - 59)
11045         },
11046         u: {
11047             g:1,
11048             c:"ms = results[{0}]; ms = parseInt(ms, 10)/Math.pow(10, ms.length - 3);\n",
11049             s:"(\\d+)" // decimal fraction of a second (minimum = 1 digit, maximum = unlimited)
11050         },
11051         O: {
11052             g:1,
11053             c:[
11054                 "o = results[{0}];",
11055                 "var sn = o.substring(0,1),", // get + / - sign
11056                     "hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60),", // get hours (performs minutes-to-hour conversion also, just in case)
11057                     "mn = o.substring(3,5) % 60;", // get minutes
11058                 "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
11059             ].join("\n"),
11060             s: "([+\-]\\d{4})" // GMT offset in hrs and mins
11061         },
11062         P: {
11063             g:1,
11064             c:[
11065                 "o = results[{0}];",
11066                 "var sn = o.substring(0,1),", // get + / - sign
11067                     "hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60),", // get hours (performs minutes-to-hour conversion also, just in case)
11068                     "mn = o.substring(4,6) % 60;", // get minutes
11069                 "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
11070             ].join("\n"),
11071             s: "([+\-]\\d{2}:\\d{2})" // GMT offset in hrs and mins (with colon separator)
11072         },
11073         T: {
11074             g:0,
11075             c:null,
11076             s:"[A-Z]{1,4}" // timezone abbrev. may be between 1 - 4 chars
11077         },
11078         Z: {
11079             g:1,
11080             c:"zz = results[{0}] * 1;\n" // -43200 <= UTC offset <= 50400
11081                   + "zz = (-43200 <= zz && zz <= 50400)? zz : null;\n",
11082             s:"([+\-]?\\d{1,5})" // leading '+' sign is optional for UTC offset
11083         },
11084         c: function() {
11085             var calc = [],
11086                 arr = [
11087                     $f("Y", 1), // year
11088                     $f("m", 2), // month
11089                     $f("d", 3), // day
11090                     $f("h", 4), // hour
11091                     $f("i", 5), // minute
11092                     $f("s", 6), // second
11093                     {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)
11094                     {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
11095                         "if(results[8]) {", // timezone specified
11096                             "if(results[8] == 'Z'){",
11097                                 "zz = 0;", // UTC
11098                             "}else if (results[8].indexOf(':') > -1){",
11099                                 $f("P", 8).c, // timezone offset with colon separator
11100                             "}else{",
11101                                 $f("O", 8).c, // timezone offset without colon separator
11102                             "}",
11103                         "}"
11104                     ].join('\n')}
11105                 ];
11106
11107             for (var i = 0, l = arr.length; i < l; ++i) {
11108                 calc.push(arr[i].c);
11109             }
11110
11111             return {
11112                 g:1,
11113                 c:calc.join(""),
11114                 s:[
11115                     arr[0].s, // year (required)
11116                     "(?:", "-", arr[1].s, // month (optional)
11117                         "(?:", "-", arr[2].s, // day (optional)
11118                             "(?:",
11119                                 "(?:T| )?", // time delimiter -- either a "T" or a single blank space
11120                                 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
11121                                 "(?::", arr[5].s, ")?", // seconds (optional)
11122                                 "(?:(?:\\.|,)(\\d+))?", // decimal fraction of a second (e.g. ",12345" or ".98765") (optional)
11123                                 "(Z|(?:[-+]\\d{2}(?::)?\\d{2}))?", // "Z" (UTC) or "-0530" (UTC offset without colon delimiter) or "+08:00" (UTC offset with colon delimiter) (optional)
11124                             ")?",
11125                         ")?",
11126                     ")?"
11127                 ].join("")
11128             }
11129         },
11130         U: {
11131             g:1,
11132             c:"u = parseInt(results[{0}], 10);\n",
11133             s:"(-?\\d+)" // leading minus sign indicates seconds before UNIX epoch
11134         }
11135     }
11136 });
11137
11138 }());
11139
11140 Ext.apply(Date.prototype, {
11141     // private
11142     dateFormat : function(format) {
11143         if (Date.formatFunctions[format] == null) {
11144             Date.createFormat(format);
11145         }
11146         return Date.formatFunctions[format].call(this);
11147     },
11148
11149     /**
11150      * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
11151      *
11152      * Note: The date string returned by the javascript Date object's toString() method varies
11153      * between browsers (e.g. FF vs IE) and system region settings (e.g. IE in Asia vs IE in America).
11154      * For a given date string e.g. "Thu Oct 25 2007 22:55:35 GMT+0800 (Malay Peninsula Standard Time)",
11155      * getTimezone() first tries to get the timezone abbreviation from between a pair of parentheses
11156      * (which may or may not be present), failing which it proceeds to get the timezone abbreviation
11157      * from the GMT offset portion of the date string.
11158      * @return {String} The abbreviated timezone name (e.g. 'CST', 'PDT', 'EDT', 'MPST' ...).
11159      */
11160     getTimezone : function() {
11161         // the following list shows the differences between date strings from different browsers on a WinXP SP2 machine from an Asian locale:
11162         //
11163         // Opera  : "Thu, 25 Oct 2007 22:53:45 GMT+0800" -- shortest (weirdest) date string of the lot
11164         // 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)
11165         // FF     : "Thu Oct 25 2007 22:55:35 GMT+0800 (Malay Peninsula Standard Time)" -- value in parentheses always gives the correct timezone
11166         // IE     : "Thu Oct 25 22:54:35 UTC+0800 2007" -- (Asian system setting) look for 3-4 letter timezone abbrev
11167         // IE     : "Thu Oct 25 17:06:37 PDT 2007" -- (American system setting) look for 3-4 letter timezone abbrev
11168         //
11169         // this crazy regex attempts to guess the correct timezone abbreviation despite these differences.
11170         // step 1: (?:\((.*)\) -- find timezone in parentheses
11171         // 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
11172         // step 3: remove all non uppercase characters found in step 1 and 2
11173         return this.toString().replace(/^.* (?:\((.*)\)|([A-Z]{1,4})(?:[\-+][0-9]{4})?(?: -?\d+)?)$/, "$1$2").replace(/[^A-Z]/g, "");
11174     },
11175
11176     /**
11177      * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
11178      * @param {Boolean} colon (optional) true to separate the hours and minutes with a colon (defaults to false).
11179      * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600').
11180      */
11181     getGMTOffset : function(colon) {
11182         return (this.getTimezoneOffset() > 0 ? "-" : "+")
11183             + String.leftPad(Math.floor(Math.abs(this.getTimezoneOffset()) / 60), 2, "0")
11184             + (colon ? ":" : "")
11185             + String.leftPad(Math.abs(this.getTimezoneOffset() % 60), 2, "0");
11186     },
11187
11188     /**
11189      * Get the numeric day number of the year, adjusted for leap year.
11190      * @return {Number} 0 to 364 (365 in leap years).
11191      */
11192     getDayOfYear: function() {
11193         var num = 0,
11194             d = this.clone(),
11195             m = this.getMonth(),
11196             i;
11197
11198         for (i = 0, d.setDate(1), d.setMonth(0); i < m; d.setMonth(++i)) {
11199             num += d.getDaysInMonth();
11200         }
11201         return num + this.getDate() - 1;
11202     },
11203
11204     /**
11205      * Get the numeric ISO-8601 week number of the year.
11206      * (equivalent to the format specifier 'W', but without a leading zero).
11207      * @return {Number} 1 to 53
11208      */
11209     getWeekOfYear : function() {
11210         // adapted from http://www.merlyn.demon.co.uk/weekcalc.htm
11211         var ms1d = 864e5, // milliseconds in a day
11212             ms7d = 7 * ms1d; // milliseconds in a week
11213
11214         return function() { // return a closure so constants get calculated only once
11215             var DC3 = Date.UTC(this.getFullYear(), this.getMonth(), this.getDate() + 3) / ms1d, // an Absolute Day Number
11216                 AWN = Math.floor(DC3 / 7), // an Absolute Week Number
11217                 Wyr = new Date(AWN * ms7d).getUTCFullYear();
11218
11219             return AWN - Math.floor(Date.UTC(Wyr, 0, 7) / ms7d) + 1;
11220         }
11221     }(),
11222
11223     /**
11224      * Checks if the current date falls within a leap year.
11225      * @return {Boolean} True if the current date falls within a leap year, false otherwise.
11226      */
11227     isLeapYear : function() {
11228         var year = this.getFullYear();
11229         return !!((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
11230     },
11231
11232     /**
11233      * Get the first day of the current month, adjusted for leap year.  The returned value
11234      * is the numeric day index within the week (0-6) which can be used in conjunction with
11235      * the {@link #monthNames} array to retrieve the textual day name.
11236      * Example:
11237      * <pre><code>
11238 var dt = new Date('1/10/2007');
11239 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
11240 </code></pre>
11241      * @return {Number} The day number (0-6).
11242      */
11243     getFirstDayOfMonth : function() {
11244         var day = (this.getDay() - (this.getDate() - 1)) % 7;
11245         return (day < 0) ? (day + 7) : day;
11246     },
11247
11248     /**
11249      * Get the last day of the current month, adjusted for leap year.  The returned value
11250      * is the numeric day index within the week (0-6) which can be used in conjunction with
11251      * the {@link #monthNames} array to retrieve the textual day name.
11252      * Example:
11253      * <pre><code>
11254 var dt = new Date('1/10/2007');
11255 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
11256 </code></pre>
11257      * @return {Number} The day number (0-6).
11258      */
11259     getLastDayOfMonth : function() {
11260         return this.getLastDateOfMonth().getDay();
11261     },
11262
11263
11264     /**
11265      * Get the date of the first day of the month in which this date resides.
11266      * @return {Date}
11267      */
11268     getFirstDateOfMonth : function() {
11269         return new Date(this.getFullYear(), this.getMonth(), 1);
11270     },
11271
11272     /**
11273      * Get the date of the last day of the month in which this date resides.
11274      * @return {Date}
11275      */
11276     getLastDateOfMonth : function() {
11277         return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
11278     },
11279
11280     /**
11281      * Get the number of days in the current month, adjusted for leap year.
11282      * @return {Number} The number of days in the month.
11283      */
11284     getDaysInMonth: function() {
11285         var daysInMonth = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
11286
11287         return function() { // return a closure for efficiency
11288             var m = this.getMonth();
11289
11290             return m == 1 && this.isLeapYear() ? 29 : daysInMonth[m];
11291         }
11292     }(),
11293
11294     /**
11295      * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
11296      * @return {String} 'st, 'nd', 'rd' or 'th'.
11297      */
11298     getSuffix : function() {
11299         switch (this.getDate()) {
11300             case 1:
11301             case 21:
11302             case 31:
11303                 return "st";
11304             case 2:
11305             case 22:
11306                 return "nd";
11307             case 3:
11308             case 23:
11309                 return "rd";
11310             default:
11311                 return "th";
11312         }
11313     },
11314
11315     /**
11316      * Creates and returns a new Date instance with the exact same date value as the called instance.
11317      * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
11318      * variable will also be changed.  When the intention is to create a new variable that will not
11319      * modify the original instance, you should create a clone.
11320      *
11321      * Example of correctly cloning a date:
11322      * <pre><code>
11323 //wrong way:
11324 var orig = new Date('10/1/2006');
11325 var copy = orig;
11326 copy.setDate(5);
11327 document.write(orig);  //returns 'Thu Oct 05 2006'!
11328
11329 //correct way:
11330 var orig = new Date('10/1/2006');
11331 var copy = orig.clone();
11332 copy.setDate(5);
11333 document.write(orig);  //returns 'Thu Oct 01 2006'
11334 </code></pre>
11335      * @return {Date} The new Date instance.
11336      */
11337     clone : function() {
11338         return new Date(this.getTime());
11339     },
11340
11341     /**
11342      * Checks if the current date is affected by Daylight Saving Time (DST).
11343      * @return {Boolean} True if the current date is affected by DST.
11344      */
11345     isDST : function() {
11346         // adapted from http://extjs.com/forum/showthread.php?p=247172#post247172
11347         // courtesy of @geoffrey.mcgill
11348         return new Date(this.getFullYear(), 0, 1).getTimezoneOffset() != this.getTimezoneOffset();
11349     },
11350
11351     /**
11352      * Attempts to clear all time information from this Date by setting the time to midnight of the same day,
11353      * automatically adjusting for Daylight Saving Time (DST) where applicable.
11354      * (note: DST timezone information for the browser's host operating system is assumed to be up-to-date)
11355      * @param {Boolean} clone true to create a clone of this date, clear the time and return it (defaults to false).
11356      * @return {Date} this or the clone.
11357      */
11358     clearTime : function(clone) {
11359         if (clone) {
11360             return this.clone().clearTime();
11361         }
11362
11363         // get current date before clearing time
11364         var d = this.getDate();
11365
11366         // clear time
11367         this.setHours(0);
11368         this.setMinutes(0);
11369         this.setSeconds(0);
11370         this.setMilliseconds(0);
11371
11372         if (this.getDate() != d) { // account for DST (i.e. day of month changed when setting hour = 0)
11373             // note: DST adjustments are assumed to occur in multiples of 1 hour (this is almost always the case)
11374             // refer to http://www.timeanddate.com/time/aboutdst.html for the (rare) exceptions to this rule
11375
11376             // increment hour until cloned date == current date
11377             for (var hr = 1, c = this.add(Date.HOUR, hr); c.getDate() != d; hr++, c = this.add(Date.HOUR, hr));
11378
11379             this.setDate(d);
11380             this.setHours(c.getHours());
11381         }
11382
11383         return this;
11384     },
11385
11386     /**
11387      * Provides a convenient method for performing basic date arithmetic. This method
11388      * does not modify the Date instance being called - it creates and returns
11389      * a new Date instance containing the resulting date value.
11390      *
11391      * Examples:
11392      * <pre><code>
11393 // Basic usage:
11394 var dt = new Date('10/29/2006').add(Date.DAY, 5);
11395 document.write(dt); //returns 'Fri Nov 03 2006 00:00:00'
11396
11397 // Negative values will be subtracted:
11398 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
11399 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
11400
11401 // You can even chain several calls together in one line:
11402 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
11403 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
11404 </code></pre>
11405      *
11406      * @param {String} interval A valid date interval enum value.
11407      * @param {Number} value The amount to add to the current date.
11408      * @return {Date} The new Date instance.
11409      */
11410     add : function(interval, value) {
11411         var d = this.clone();
11412         if (!interval || value === 0) return d;
11413
11414         switch(interval.toLowerCase()) {
11415             case Date.MILLI:
11416                 d.setMilliseconds(this.getMilliseconds() + value);
11417                 break;
11418             case Date.SECOND:
11419                 d.setSeconds(this.getSeconds() + value);
11420                 break;
11421             case Date.MINUTE:
11422                 d.setMinutes(this.getMinutes() + value);
11423                 break;
11424             case Date.HOUR:
11425                 d.setHours(this.getHours() + value);
11426                 break;
11427             case Date.DAY:
11428                 d.setDate(this.getDate() + value);
11429                 break;
11430             case Date.MONTH:
11431                 var day = this.getDate();
11432                 if (day > 28) {
11433                     day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
11434                 }
11435                 d.setDate(day);
11436                 d.setMonth(this.getMonth() + value);
11437                 break;
11438             case Date.YEAR:
11439                 d.setFullYear(this.getFullYear() + value);
11440                 break;
11441         }
11442         return d;
11443     },
11444
11445     /**
11446      * Checks if this date falls on or between the given start and end dates.
11447      * @param {Date} start Start date
11448      * @param {Date} end End date
11449      * @return {Boolean} true if this date falls on or between the given start and end dates.
11450      */
11451     between : function(start, end) {
11452         var t = this.getTime();
11453         return start.getTime() <= t && t <= end.getTime();
11454     }
11455 });
11456
11457
11458 /**
11459  * Formats a date given the supplied format string.
11460  * @param {String} format The format string.
11461  * @return {String} The formatted date.
11462  * @method format
11463  */
11464 Date.prototype.format = Date.prototype.dateFormat;
11465
11466
11467 // private
11468 if (Ext.isSafari && (navigator.userAgent.match(/WebKit\/(\d+)/)[1] || NaN) < 420) {
11469     Ext.apply(Date.prototype, {
11470         _xMonth : Date.prototype.setMonth,
11471         _xDate  : Date.prototype.setDate,
11472
11473         // Bug in Safari 1.3, 2.0 (WebKit build < 420)
11474         // Date.setMonth does not work consistently if iMonth is not 0-11
11475         setMonth : function(num) {
11476             if (num <= -1) {
11477                 var n = Math.ceil(-num),
11478                     back_year = Math.ceil(n / 12),
11479                     month = (n % 12) ? 12 - n % 12 : 0;
11480
11481                 this.setFullYear(this.getFullYear() - back_year);
11482
11483                 return this._xMonth(month);
11484             } else {
11485                 return this._xMonth(num);
11486             }
11487         },
11488
11489         // Bug in setDate() method (resolved in WebKit build 419.3, so to be safe we target Webkit builds < 420)
11490         // The parameter for Date.setDate() is converted to a signed byte integer in Safari
11491         // http://brianary.blogspot.com/2006/03/safari-date-bug.html
11492         setDate : function(d) {
11493             // use setTime() to workaround setDate() bug
11494             // subtract current day of month in milliseconds, then add desired day of month in milliseconds
11495             return this.setTime(this.getTime() - (this.getDate() - d) * 864e5);
11496         }
11497     });
11498 }
11499
11500
11501
11502 /* Some basic Date tests... (requires Firebug)
11503
11504 Date.parseDate('', 'c'); // call Date.parseDate() once to force computation of regex string so we can console.log() it
11505 console.log('Insane Regex for "c" format: %o', Date.parseCodes.c.s); // view the insane regex for the "c" format specifier
11506
11507 // standard tests
11508 console.group('Standard Date.parseDate() Tests');
11509     console.log('Date.parseDate("2009-01-05T11:38:56", "c")               = %o', Date.parseDate("2009-01-05T11:38:56", "c")); // assumes browser's timezone setting
11510     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
11511     console.log('Date.parseDate("2009-03-03T13:36:54,101000Z", "c")       = %o', Date.parseDate("2009-03-03T13:36:54,101000Z", "c")); // UTC
11512     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
11513     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
11514 console.groupEnd();
11515
11516 // ISO-8601 format as specified in http://www.w3.org/TR/NOTE-datetime
11517 // -- accepts ALL 6 levels of date-time granularity
11518 console.group('ISO-8601 Granularity Test (see http://www.w3.org/TR/NOTE-datetime)');
11519     console.log('Date.parseDate("1997", "c")                              = %o', Date.parseDate("1997", "c")); // YYYY (e.g. 1997)
11520     console.log('Date.parseDate("1997-07", "c")                           = %o', Date.parseDate("1997-07", "c")); // YYYY-MM (e.g. 1997-07)
11521     console.log('Date.parseDate("1997-07-16", "c")                        = %o', Date.parseDate("1997-07-16", "c")); // YYYY-MM-DD (e.g. 1997-07-16)
11522     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)
11523     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)
11524     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)
11525     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)
11526     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
11527 console.groupEnd();
11528
11529 //*//**
11530  * @class Ext.util.MixedCollection
11531  * @extends Ext.util.Observable
11532  * A Collection class that maintains both numeric indexes and keys and exposes events.
11533  * @constructor
11534  * @param {Boolean} allowFunctions Specify <tt>true</tt> if the {@link #addAll}
11535  * function should add function references to the collection. Defaults to
11536  * <tt>false</tt>.
11537  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
11538  * and return the key value for that item.  This is used when available to look up the key on items that
11539  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
11540  * equivalent to providing an implementation for the {@link #getKey} method.
11541  */
11542 Ext.util.MixedCollection = function(allowFunctions, keyFn){
11543     this.items = [];
11544     this.map = {};
11545     this.keys = [];
11546     this.length = 0;
11547     this.addEvents(
11548         /**
11549          * @event clear
11550          * Fires when the collection is cleared.
11551          */
11552         'clear',
11553         /**
11554          * @event add
11555          * Fires when an item is added to the collection.
11556          * @param {Number} index The index at which the item was added.
11557          * @param {Object} o The item added.
11558          * @param {String} key The key associated with the added item.
11559          */
11560         'add',
11561         /**
11562          * @event replace
11563          * Fires when an item is replaced in the collection.
11564          * @param {String} key he key associated with the new added.
11565          * @param {Object} old The item being replaced.
11566          * @param {Object} new The new item.
11567          */
11568         'replace',
11569         /**
11570          * @event remove
11571          * Fires when an item is removed from the collection.
11572          * @param {Object} o The item being removed.
11573          * @param {String} key (optional) The key associated with the removed item.
11574          */
11575         'remove',
11576         'sort'
11577     );
11578     this.allowFunctions = allowFunctions === true;
11579     if(keyFn){
11580         this.getKey = keyFn;
11581     }
11582     Ext.util.MixedCollection.superclass.constructor.call(this);
11583 };
11584
11585 Ext.extend(Ext.util.MixedCollection, Ext.util.Observable, {
11586
11587     /**
11588      * @cfg {Boolean} allowFunctions Specify <tt>true</tt> if the {@link #addAll}
11589      * function should add function references to the collection. Defaults to
11590      * <tt>false</tt>.
11591      */
11592     allowFunctions : false,
11593
11594     /**
11595      * Adds an item to the collection. Fires the {@link #add} event when complete.
11596      * @param {String} key <p>The key to associate with the item, or the new item.</p>
11597      * <p>If a {@link #getKey} implementation was specified for this MixedCollection,
11598      * or if the key of the stored items is in a property called <tt><b>id</b></tt>,
11599      * the MixedCollection will be able to <i>derive</i> the key for the new item.
11600      * In this case just pass the new item in this parameter.</p>
11601      * @param {Object} o The item to add.
11602      * @return {Object} The item added.
11603      */
11604     add : function(key, o){
11605         if(arguments.length == 1){
11606             o = arguments[0];
11607             key = this.getKey(o);
11608         }
11609         if(typeof key != 'undefined' && key !== null){
11610             var old = this.map[key];
11611             if(typeof old != 'undefined'){
11612                 return this.replace(key, o);
11613             }
11614             this.map[key] = o;
11615         }
11616         this.length++;
11617         this.items.push(o);
11618         this.keys.push(key);
11619         this.fireEvent('add', this.length-1, o, key);
11620         return o;
11621     },
11622
11623     /**
11624       * MixedCollection has a generic way to fetch keys if you implement getKey.  The default implementation
11625       * simply returns <b><code>item.id</code></b> but you can provide your own implementation
11626       * to return a different value as in the following examples:<pre><code>
11627 // normal way
11628 var mc = new Ext.util.MixedCollection();
11629 mc.add(someEl.dom.id, someEl);
11630 mc.add(otherEl.dom.id, otherEl);
11631 //and so on
11632
11633 // using getKey
11634 var mc = new Ext.util.MixedCollection();
11635 mc.getKey = function(el){
11636    return el.dom.id;
11637 };
11638 mc.add(someEl);
11639 mc.add(otherEl);
11640
11641 // or via the constructor
11642 var mc = new Ext.util.MixedCollection(false, function(el){
11643    return el.dom.id;
11644 });
11645 mc.add(someEl);
11646 mc.add(otherEl);
11647      * </code></pre>
11648      * @param {Object} item The item for which to find the key.
11649      * @return {Object} The key for the passed item.
11650      */
11651     getKey : function(o){
11652          return o.id;
11653     },
11654
11655     /**
11656      * Replaces an item in the collection. Fires the {@link #replace} event when complete.
11657      * @param {String} key <p>The key associated with the item to replace, or the replacement item.</p>
11658      * <p>If you supplied a {@link #getKey} implementation for this MixedCollection, or if the key
11659      * of your stored items is in a property called <tt><b>id</b></tt>, then the MixedCollection
11660      * will be able to <i>derive</i> the key of the replacement item. If you want to replace an item
11661      * with one having the same key value, then just pass the replacement item in this parameter.</p>
11662      * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate
11663      * with that key.
11664      * @return {Object}  The new item.
11665      */
11666     replace : function(key, o){
11667         if(arguments.length == 1){
11668             o = arguments[0];
11669             key = this.getKey(o);
11670         }
11671         var old = this.map[key];
11672         if(typeof key == 'undefined' || key === null || typeof old == 'undefined'){
11673              return this.add(key, o);
11674         }
11675         var index = this.indexOfKey(key);
11676         this.items[index] = o;
11677         this.map[key] = o;
11678         this.fireEvent('replace', key, old, o);
11679         return o;
11680     },
11681
11682     /**
11683      * Adds all elements of an Array or an Object to the collection.
11684      * @param {Object/Array} objs An Object containing properties which will be added
11685      * to the collection, or an Array of values, each of which are added to the collection.
11686      * Functions references will be added to the collection if <code>{@link #allowFunctions}</code>
11687      * has been set to <tt>true</tt>.
11688      */
11689     addAll : function(objs){
11690         if(arguments.length > 1 || Ext.isArray(objs)){
11691             var args = arguments.length > 1 ? arguments : objs;
11692             for(var i = 0, len = args.length; i < len; i++){
11693                 this.add(args[i]);
11694             }
11695         }else{
11696             for(var key in objs){
11697                 if(this.allowFunctions || typeof objs[key] != 'function'){
11698                     this.add(key, objs[key]);
11699                 }
11700             }
11701         }
11702     },
11703
11704     /**
11705      * Executes the specified function once for every item in the collection, passing the following arguments:
11706      * <div class="mdetail-params"><ul>
11707      * <li><b>item</b> : Mixed<p class="sub-desc">The collection item</p></li>
11708      * <li><b>index</b> : Number<p class="sub-desc">The item's index</p></li>
11709      * <li><b>length</b> : Number<p class="sub-desc">The total number of items in the collection</p></li>
11710      * </ul></div>
11711      * The function should return a boolean value. Returning false from the function will stop the iteration.
11712      * @param {Function} fn The function to execute for each item.
11713      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the current item in the iteration.
11714      */
11715     each : function(fn, scope){
11716         var items = [].concat(this.items); // each safe for removal
11717         for(var i = 0, len = items.length; i < len; i++){
11718             if(fn.call(scope || items[i], items[i], i, len) === false){
11719                 break;
11720             }
11721         }
11722     },
11723
11724     /**
11725      * Executes the specified function once for every key in the collection, passing each
11726      * key, and its associated item as the first two parameters.
11727      * @param {Function} fn The function to execute for each item.
11728      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the browser window.
11729      */
11730     eachKey : function(fn, scope){
11731         for(var i = 0, len = this.keys.length; i < len; i++){
11732             fn.call(scope || window, this.keys[i], this.items[i], i, len);
11733         }
11734     },
11735
11736     /**
11737      * Returns the first item in the collection which elicits a true return value from the
11738      * passed selection function.
11739      * @param {Function} fn The selection function to execute for each item.
11740      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the browser window.
11741      * @return {Object} The first item in the collection which returned true from the selection function.
11742      */
11743     find : function(fn, scope){
11744         for(var i = 0, len = this.items.length; i < len; i++){
11745             if(fn.call(scope || window, this.items[i], this.keys[i])){
11746                 return this.items[i];
11747             }
11748         }
11749         return null;
11750     },
11751
11752     /**
11753      * Inserts an item at the specified index in the collection. Fires the {@link #add} event when complete.
11754      * @param {Number} index The index to insert the item at.
11755      * @param {String} key The key to associate with the new item, or the item itself.
11756      * @param {Object} o (optional) If the second parameter was a key, the new item.
11757      * @return {Object} The item inserted.
11758      */
11759     insert : function(index, key, o){
11760         if(arguments.length == 2){
11761             o = arguments[1];
11762             key = this.getKey(o);
11763         }
11764         if(this.containsKey(key)){
11765             this.suspendEvents();
11766             this.removeKey(key);
11767             this.resumeEvents();
11768         }
11769         if(index >= this.length){
11770             return this.add(key, o);
11771         }
11772         this.length++;
11773         this.items.splice(index, 0, o);
11774         if(typeof key != 'undefined' && key !== null){
11775             this.map[key] = o;
11776         }
11777         this.keys.splice(index, 0, key);
11778         this.fireEvent('add', index, o, key);
11779         return o;
11780     },
11781
11782     /**
11783      * Remove an item from the collection.
11784      * @param {Object} o The item to remove.
11785      * @return {Object} The item removed or false if no item was removed.
11786      */
11787     remove : function(o){
11788         return this.removeAt(this.indexOf(o));
11789     },
11790
11791     /**
11792      * Remove an item from a specified index in the collection. Fires the {@link #remove} event when complete.
11793      * @param {Number} index The index within the collection of the item to remove.
11794      * @return {Object} The item removed or false if no item was removed.
11795      */
11796     removeAt : function(index){
11797         if(index < this.length && index >= 0){
11798             this.length--;
11799             var o = this.items[index];
11800             this.items.splice(index, 1);
11801             var key = this.keys[index];
11802             if(typeof key != 'undefined'){
11803                 delete this.map[key];
11804             }
11805             this.keys.splice(index, 1);
11806             this.fireEvent('remove', o, key);
11807             return o;
11808         }
11809         return false;
11810     },
11811
11812     /**
11813      * Removed an item associated with the passed key fom the collection.
11814      * @param {String} key The key of the item to remove.
11815      * @return {Object} The item removed or false if no item was removed.
11816      */
11817     removeKey : function(key){
11818         return this.removeAt(this.indexOfKey(key));
11819     },
11820
11821     /**
11822      * Returns the number of items in the collection.
11823      * @return {Number} the number of items in the collection.
11824      */
11825     getCount : function(){
11826         return this.length;
11827     },
11828
11829     /**
11830      * Returns index within the collection of the passed Object.
11831      * @param {Object} o The item to find the index of.
11832      * @return {Number} index of the item. Returns -1 if not found.
11833      */
11834     indexOf : function(o){
11835         return this.items.indexOf(o);
11836     },
11837
11838     /**
11839      * Returns index within the collection of the passed key.
11840      * @param {String} key The key to find the index of.
11841      * @return {Number} index of the key.
11842      */
11843     indexOfKey : function(key){
11844         return this.keys.indexOf(key);
11845     },
11846
11847     /**
11848      * Returns the item associated with the passed key OR index.
11849      * Key has priority over index.  This is the equivalent
11850      * of calling {@link #key} first, then if nothing matched calling {@link #itemAt}.
11851      * @param {String/Number} key The key or index of the item.
11852      * @return {Object} If the item is found, returns the item.  If the item was not found, returns <tt>undefined</tt>.
11853      * If an item was found, but is a Class, returns <tt>null</tt>.
11854      */
11855     item : function(key){
11856         var mk = this.map[key],
11857             item = mk !== undefined ? mk : (typeof key == 'number') ? this.items[key] : undefined;
11858         return !Ext.isFunction(item) || this.allowFunctions ? item : null; // for prototype!
11859     },
11860
11861     /**
11862      * Returns the item at the specified index.
11863      * @param {Number} index The index of the item.
11864      * @return {Object} The item at the specified index.
11865      */
11866     itemAt : function(index){
11867         return this.items[index];
11868     },
11869
11870     /**
11871      * Returns the item associated with the passed key.
11872      * @param {String/Number} key The key of the item.
11873      * @return {Object} The item associated with the passed key.
11874      */
11875     key : function(key){
11876         return this.map[key];
11877     },
11878
11879     /**
11880      * Returns true if the collection contains the passed Object as an item.
11881      * @param {Object} o  The Object to look for in the collection.
11882      * @return {Boolean} True if the collection contains the Object as an item.
11883      */
11884     contains : function(o){
11885         return this.indexOf(o) != -1;
11886     },
11887
11888     /**
11889      * Returns true if the collection contains the passed Object as a key.
11890      * @param {String} key The key to look for in the collection.
11891      * @return {Boolean} True if the collection contains the Object as a key.
11892      */
11893     containsKey : function(key){
11894         return typeof this.map[key] != 'undefined';
11895     },
11896
11897     /**
11898      * Removes all items from the collection.  Fires the {@link #clear} event when complete.
11899      */
11900     clear : function(){
11901         this.length = 0;
11902         this.items = [];
11903         this.keys = [];
11904         this.map = {};
11905         this.fireEvent('clear');
11906     },
11907
11908     /**
11909      * Returns the first item in the collection.
11910      * @return {Object} the first item in the collection..
11911      */
11912     first : function(){
11913         return this.items[0];
11914     },
11915
11916     /**
11917      * Returns the last item in the collection.
11918      * @return {Object} the last item in the collection..
11919      */
11920     last : function(){
11921         return this.items[this.length-1];
11922     },
11923
11924     /**
11925      * @private
11926      * @param {String} property Property to sort by ('key', 'value', or 'index')
11927      * @param {String} dir (optional) Direction to sort 'ASC' or 'DESC'. Defaults to 'ASC'.
11928      * @param {Function} fn (optional) Comparison function that defines the sort order.
11929      * Defaults to sorting by numeric value.
11930      */
11931     _sort : function(property, dir, fn){
11932         var i,
11933             len,
11934             dsc = String(dir).toUpperCase() == 'DESC' ? -1 : 1,
11935             c = [], k = this.keys, items = this.items;
11936
11937         fn = fn || function(a, b){
11938             return a-b;
11939         };
11940         for(i = 0, len = items.length; i < len; i++){
11941             c[c.length] = {key: k[i], value: items[i], index: i};
11942         }
11943         c.sort(function(a, b){
11944             var v = fn(a[property], b[property]) * dsc;
11945             if(v === 0){
11946                 v = (a.index < b.index ? -1 : 1);
11947             }
11948             return v;
11949         });
11950         for(i = 0, len = c.length; i < len; i++){
11951             items[i] = c[i].value;
11952             k[i] = c[i].key;
11953         }
11954         this.fireEvent('sort', this);
11955     },
11956
11957     /**
11958      * Sorts this collection by <b>item</b> value with the passed comparison function.
11959      * @param {String} direction (optional) 'ASC' or 'DESC'. Defaults to 'ASC'.
11960      * @param {Function} fn (optional) Comparison function that defines the sort order.
11961      * Defaults to sorting by numeric value.
11962      */
11963     sort : function(dir, fn){
11964         this._sort('value', dir, fn);
11965     },
11966
11967     /**
11968      * Sorts this collection by <b>key</b>s.
11969      * @param {String} direction (optional) 'ASC' or 'DESC'. Defaults to 'ASC'.
11970      * @param {Function} fn (optional) Comparison function that defines the sort order.
11971      * Defaults to sorting by case insensitive string.
11972      */
11973     keySort : function(dir, fn){
11974         this._sort('key', dir, fn || function(a, b){
11975             var v1 = String(a).toUpperCase(), v2 = String(b).toUpperCase();
11976             return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11977         });
11978     },
11979
11980     /**
11981      * Returns a range of items in this collection
11982      * @param {Number} startIndex (optional) The starting index. Defaults to 0.
11983      * @param {Number} endIndex (optional) The ending index. Defaults to the last item.
11984      * @return {Array} An array of items
11985      */
11986     getRange : function(start, end){
11987         var items = this.items;
11988         if(items.length < 1){
11989             return [];
11990         }
11991         start = start || 0;
11992         end = Math.min(typeof end == 'undefined' ? this.length-1 : end, this.length-1);
11993         var i, r = [];
11994         if(start <= end){
11995             for(i = start; i <= end; i++) {
11996                 r[r.length] = items[i];
11997             }
11998         }else{
11999             for(i = start; i >= end; i--) {
12000                 r[r.length] = items[i];
12001             }
12002         }
12003         return r;
12004     },
12005
12006     /**
12007      * Filter the <i>objects</i> in this collection by a specific property.
12008      * Returns a new collection that has been filtered.
12009      * @param {String} property A property on your objects
12010      * @param {String/RegExp} value Either string that the property values
12011      * should start with or a RegExp to test against the property
12012      * @param {Boolean} anyMatch (optional) True to match any part of the string, not just the beginning
12013      * @param {Boolean} caseSensitive (optional) True for case sensitive comparison (defaults to False).
12014      * @return {MixedCollection} The new filtered collection
12015      */
12016     filter : function(property, value, anyMatch, caseSensitive){
12017         if(Ext.isEmpty(value, false)){
12018             return this.clone();
12019         }
12020         value = this.createValueMatcher(value, anyMatch, caseSensitive);
12021         return this.filterBy(function(o){
12022             return o && value.test(o[property]);
12023         });
12024     },
12025
12026     /**
12027      * Filter by a function. Returns a <i>new</i> collection that has been filtered.
12028      * The passed function will be called with each object in the collection.
12029      * If the function returns true, the value is included otherwise it is filtered.
12030      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
12031      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to this MixedCollection.
12032      * @return {MixedCollection} The new filtered collection
12033      */
12034     filterBy : function(fn, scope){
12035         var r = new Ext.util.MixedCollection();
12036         r.getKey = this.getKey;
12037         var k = this.keys, it = this.items;
12038         for(var i = 0, len = it.length; i < len; i++){
12039             if(fn.call(scope||this, it[i], k[i])){
12040                 r.add(k[i], it[i]);
12041             }
12042         }
12043         return r;
12044     },
12045
12046     /**
12047      * Finds the index of the first matching object in this collection by a specific property/value.
12048      * @param {String} property The name of a property on your objects.
12049      * @param {String/RegExp} value A string that the property values
12050      * should start with or a RegExp to test against the property.
12051      * @param {Number} start (optional) The index to start searching at (defaults to 0).
12052      * @param {Boolean} anyMatch (optional) True to match any part of the string, not just the beginning.
12053      * @param {Boolean} caseSensitive (optional) True for case sensitive comparison.
12054      * @return {Number} The matched index or -1
12055      */
12056     findIndex : function(property, value, start, anyMatch, caseSensitive){
12057         if(Ext.isEmpty(value, false)){
12058             return -1;
12059         }
12060         value = this.createValueMatcher(value, anyMatch, caseSensitive);
12061         return this.findIndexBy(function(o){
12062             return o && value.test(o[property]);
12063         }, null, start);
12064     },
12065
12066     /**
12067      * Find the index of the first matching object in this collection by a function.
12068      * If the function returns <i>true</i> it is considered a match.
12069      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key).
12070      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to this MixedCollection.
12071      * @param {Number} start (optional) The index to start searching at (defaults to 0).
12072      * @return {Number} The matched index or -1
12073      */
12074     findIndexBy : function(fn, scope, start){
12075         var k = this.keys, it = this.items;
12076         for(var i = (start||0), len = it.length; i < len; i++){
12077             if(fn.call(scope||this, it[i], k[i])){
12078                 return i;
12079             }
12080         }
12081         return -1;
12082     },
12083
12084     // private
12085     createValueMatcher : function(value, anyMatch, caseSensitive, exactMatch) {
12086         if (!value.exec) { // not a regex
12087             var er = Ext.escapeRe;
12088             value = String(value);
12089             if (anyMatch === true) {
12090                 value = er(value);
12091             } else {
12092                 value = '^' + er(value);
12093                 if (exactMatch === true) {
12094                     value += '$';
12095                 }
12096             }
12097             value = new RegExp(value, caseSensitive ? '' : 'i');
12098          }
12099          return value;
12100     },
12101
12102     /**
12103      * Creates a shallow copy of this collection
12104      * @return {MixedCollection}
12105      */
12106     clone : function(){
12107         var r = new Ext.util.MixedCollection();
12108         var k = this.keys, it = this.items;
12109         for(var i = 0, len = it.length; i < len; i++){
12110             r.add(k[i], it[i]);
12111         }
12112         r.getKey = this.getKey;
12113         return r;
12114     }
12115 });
12116 /**
12117  * This method calls {@link #item item()}.
12118  * Returns the item associated with the passed key OR index. Key has priority
12119  * over index.  This is the equivalent of calling {@link #key} first, then if
12120  * nothing matched calling {@link #itemAt}.
12121  * @param {String/Number} key The key or index of the item.
12122  * @return {Object} If the item is found, returns the item.  If the item was
12123  * not found, returns <tt>undefined</tt>. If an item was found, but is a Class,
12124  * returns <tt>null</tt>.
12125  */
12126 Ext.util.MixedCollection.prototype.get = Ext.util.MixedCollection.prototype.item;/**
12127  * @class Ext.util.JSON
12128  * Modified version of Douglas Crockford"s json.js that doesn"t
12129  * mess with the Object prototype
12130  * http://www.json.org/js.html
12131  * @singleton
12132  */
12133 Ext.util.JSON = new (function(){
12134     var useHasOwn = !!{}.hasOwnProperty,
12135         isNative = function() {
12136             var useNative = null;
12137
12138             return function() {
12139                 if (useNative === null) {
12140                     useNative = Ext.USE_NATIVE_JSON && window.JSON && JSON.toString() == '[object JSON]';
12141                 }
12142         
12143                 return useNative;
12144             };
12145         }(),
12146         pad = function(n) {
12147             return n < 10 ? "0" + n : n;
12148         },
12149         doDecode = function(json){
12150             return eval("(" + json + ')');    
12151         },
12152         doEncode = function(o){
12153             if(!Ext.isDefined(o) || o === null){
12154                 return "null";
12155             }else if(Ext.isArray(o)){
12156                 return encodeArray(o);
12157             }else if(Ext.isDate(o)){
12158                 return Ext.util.JSON.encodeDate(o);
12159             }else if(Ext.isString(o)){
12160                 return encodeString(o);
12161             }else if(typeof o == "number"){
12162                 //don't use isNumber here, since finite checks happen inside isNumber
12163                 return isFinite(o) ? String(o) : "null";
12164             }else if(Ext.isBoolean(o)){
12165                 return String(o);
12166             }else {
12167                 var a = ["{"], b, i, v;
12168                 for (i in o) {
12169                     // don't encode DOM objects
12170                     if(!o.getElementsByTagName){
12171                         if(!useHasOwn || o.hasOwnProperty(i)) {
12172                             v = o[i];
12173                             switch (typeof v) {
12174                             case "undefined":
12175                             case "function":
12176                             case "unknown":
12177                                 break;
12178                             default:
12179                                 if(b){
12180                                     a.push(',');
12181                                 }
12182                                 a.push(doEncode(i), ":",
12183                                         v === null ? "null" : doEncode(v));
12184                                 b = true;
12185                             }
12186                         }
12187                     }
12188                 }
12189                 a.push("}");
12190                 return a.join("");
12191             }    
12192         },
12193         m = {
12194             "\b": '\\b',
12195             "\t": '\\t',
12196             "\n": '\\n',
12197             "\f": '\\f',
12198             "\r": '\\r',
12199             '"' : '\\"',
12200             "\\": '\\\\'
12201         },
12202         encodeString = function(s){
12203             if (/["\\\x00-\x1f]/.test(s)) {
12204                 return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
12205                     var c = m[b];
12206                     if(c){
12207                         return c;
12208                     }
12209                     c = b.charCodeAt();
12210                     return "\\u00" +
12211                         Math.floor(c / 16).toString(16) +
12212                         (c % 16).toString(16);
12213                 }) + '"';
12214             }
12215             return '"' + s + '"';
12216         },
12217         encodeArray = function(o){
12218             var a = ["["], b, i, l = o.length, v;
12219                 for (i = 0; i < l; i += 1) {
12220                     v = o[i];
12221                     switch (typeof v) {
12222                         case "undefined":
12223                         case "function":
12224                         case "unknown":
12225                             break;
12226                         default:
12227                             if (b) {
12228                                 a.push(',');
12229                             }
12230                             a.push(v === null ? "null" : Ext.util.JSON.encode(v));
12231                             b = true;
12232                     }
12233                 }
12234                 a.push("]");
12235                 return a.join("");
12236         };
12237
12238     this.encodeDate = function(o){
12239         return '"' + o.getFullYear() + "-" +
12240                 pad(o.getMonth() + 1) + "-" +
12241                 pad(o.getDate()) + "T" +
12242                 pad(o.getHours()) + ":" +
12243                 pad(o.getMinutes()) + ":" +
12244                 pad(o.getSeconds()) + '"';
12245     };
12246
12247     /**
12248      * Encodes an Object, Array or other value
12249      * @param {Mixed} o The variable to encode
12250      * @return {String} The JSON string
12251      */
12252     this.encode = function() {
12253         var ec;
12254         return function(o) {
12255             if (!ec) {
12256                 // setup encoding function on first access
12257                 ec = isNative() ? JSON.stringify : doEncode;
12258             }
12259             return ec(o);
12260         };
12261     }();
12262
12263
12264     /**
12265      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError unless the safe option is set.
12266      * @param {String} json The JSON string
12267      * @return {Object} The resulting object
12268      */
12269     this.decode = function() {
12270         var dc;
12271         return function(json) {
12272             if (!dc) {
12273                 // setup decoding function on first access
12274                 dc = isNative() ? JSON.parse : doDecode;
12275             }
12276             return dc(json);
12277         };
12278     }();
12279
12280 })();
12281 /**
12282  * Shorthand for {@link Ext.util.JSON#encode}
12283  * @param {Mixed} o The variable to encode
12284  * @return {String} The JSON string
12285  * @member Ext
12286  * @method encode
12287  */
12288 Ext.encode = Ext.util.JSON.encode;
12289 /**
12290  * Shorthand for {@link Ext.util.JSON#decode}
12291  * @param {String} json The JSON string
12292  * @param {Boolean} safe (optional) Whether to return null or throw an exception if the JSON is invalid.
12293  * @return {Object} The resulting object
12294  * @member Ext
12295  * @method decode
12296  */
12297 Ext.decode = Ext.util.JSON.decode;
12298 /**\r
12299  * @class Ext.util.Format\r
12300  * Reusable data formatting functions\r
12301  * @singleton\r
12302  */\r
12303 Ext.util.Format = function(){\r
12304     var trimRe = /^\s+|\s+$/g,\r
12305         stripTagsRE = /<\/?[^>]+>/gi,\r
12306         stripScriptsRe = /(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig,\r
12307         nl2brRe = /\r?\n/g;\r
12308         \r
12309     return {\r
12310         /**\r
12311          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length\r
12312          * @param {String} value The string to truncate\r
12313          * @param {Number} length The maximum length to allow before truncating\r
12314          * @param {Boolean} word True to try to find a common work break\r
12315          * @return {String} The converted text\r
12316          */\r
12317         ellipsis : function(value, len, word){\r
12318             if(value && value.length > len){\r
12319                 if(word){\r
12320                     var vs = value.substr(0, len - 2),\r
12321                         index = Math.max(vs.lastIndexOf(' '), vs.lastIndexOf('.'), vs.lastIndexOf('!'), vs.lastIndexOf('?'));\r
12322                     if(index == -1 || index < (len - 15)){\r
12323                         return value.substr(0, len - 3) + "...";\r
12324                     }else{\r
12325                         return vs.substr(0, index) + "...";\r
12326                     }\r
12327                 } else{\r
12328                     return value.substr(0, len - 3) + "...";\r
12329                 }\r
12330             }\r
12331             return value;\r
12332         },\r
12333 \r
12334         /**\r
12335          * Checks a reference and converts it to empty string if it is undefined\r
12336          * @param {Mixed} value Reference to check\r
12337          * @return {Mixed} Empty string if converted, otherwise the original value\r
12338          */\r
12339         undef : function(value){\r
12340             return value !== undefined ? value : "";\r
12341         },\r
12342 \r
12343         /**\r
12344          * Checks a reference and converts it to the default value if it's empty\r
12345          * @param {Mixed} value Reference to check\r
12346          * @param {String} defaultValue The value to insert of it's undefined (defaults to "")\r
12347          * @return {String}\r
12348          */\r
12349         defaultValue : function(value, defaultValue){\r
12350             return value !== undefined && value !== '' ? value : defaultValue;\r
12351         },\r
12352 \r
12353         /**\r
12354          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.\r
12355          * @param {String} value The string to encode\r
12356          * @return {String} The encoded text\r
12357          */\r
12358         htmlEncode : function(value){\r
12359             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");\r
12360         },\r
12361 \r
12362         /**\r
12363          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.\r
12364          * @param {String} value The string to decode\r
12365          * @return {String} The decoded text\r
12366          */\r
12367         htmlDecode : function(value){\r
12368             return !value ? value : String(value).replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"').replace(/&amp;/g, "&");\r
12369         },\r
12370 \r
12371         /**\r
12372          * Trims any whitespace from either side of a string\r
12373          * @param {String} value The text to trim\r
12374          * @return {String} The trimmed text\r
12375          */\r
12376         trim : function(value){\r
12377             return String(value).replace(trimRe, "");\r
12378         },\r
12379 \r
12380         /**\r
12381          * Returns a substring from within an original string\r
12382          * @param {String} value The original text\r
12383          * @param {Number} start The start index of the substring\r
12384          * @param {Number} length The length of the substring\r
12385          * @return {String} The substring\r
12386          */\r
12387         substr : function(value, start, length){\r
12388             return String(value).substr(start, length);\r
12389         },\r
12390 \r
12391         /**\r
12392          * Converts a string to all lower case letters\r
12393          * @param {String} value The text to convert\r
12394          * @return {String} The converted text\r
12395          */\r
12396         lowercase : function(value){\r
12397             return String(value).toLowerCase();\r
12398         },\r
12399 \r
12400         /**\r
12401          * Converts a string to all upper case letters\r
12402          * @param {String} value The text to convert\r
12403          * @return {String} The converted text\r
12404          */\r
12405         uppercase : function(value){\r
12406             return String(value).toUpperCase();\r
12407         },\r
12408 \r
12409         /**\r
12410          * Converts the first character only of a string to upper case\r
12411          * @param {String} value The text to convert\r
12412          * @return {String} The converted text\r
12413          */\r
12414         capitalize : function(value){\r
12415             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();\r
12416         },\r
12417 \r
12418         // private\r
12419         call : function(value, fn){\r
12420             if(arguments.length > 2){\r
12421                 var args = Array.prototype.slice.call(arguments, 2);\r
12422                 args.unshift(value);\r
12423                 return eval(fn).apply(window, args);\r
12424             }else{\r
12425                 return eval(fn).call(window, value);\r
12426             }\r
12427         },\r
12428 \r
12429         /**\r
12430          * Format a number as US currency\r
12431          * @param {Number/String} value The numeric value to format\r
12432          * @return {String} The formatted currency string\r
12433          */\r
12434         usMoney : function(v){\r
12435             v = (Math.round((v-0)*100))/100;\r
12436             v = (v == Math.floor(v)) ? v + ".00" : ((v*10 == Math.floor(v*10)) ? v + "0" : v);\r
12437             v = String(v);\r
12438             var ps = v.split('.'),\r
12439                 whole = ps[0],\r
12440                 sub = ps[1] ? '.'+ ps[1] : '.00',\r
12441                 r = /(\d+)(\d{3})/;\r
12442             while (r.test(whole)) {\r
12443                 whole = whole.replace(r, '$1' + ',' + '$2');\r
12444             }\r
12445             v = whole + sub;\r
12446             if(v.charAt(0) == '-'){\r
12447                 return '-$' + v.substr(1);\r
12448             }\r
12449             return "$" +  v;\r
12450         },\r
12451 \r
12452         /**\r
12453          * Parse a value into a formatted date using the specified format pattern.\r
12454          * @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
12455          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')\r
12456          * @return {String} The formatted date string\r
12457          */\r
12458         date : function(v, format){\r
12459             if(!v){\r
12460                 return "";\r
12461             }\r
12462             if(!Ext.isDate(v)){\r
12463                 v = new Date(Date.parse(v));\r
12464             }\r
12465             return v.dateFormat(format || "m/d/Y");\r
12466         },\r
12467 \r
12468         /**\r
12469          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently\r
12470          * @param {String} format Any valid date format string\r
12471          * @return {Function} The date formatting function\r
12472          */\r
12473         dateRenderer : function(format){\r
12474             return function(v){\r
12475                 return Ext.util.Format.date(v, format);\r
12476             };\r
12477         },\r
12478         \r
12479         /**\r
12480          * Strips all HTML tags\r
12481          * @param {Mixed} value The text from which to strip tags\r
12482          * @return {String} The stripped text\r
12483          */\r
12484         stripTags : function(v){\r
12485             return !v ? v : String(v).replace(stripTagsRE, "");\r
12486         },\r
12487 \r
12488         /**\r
12489          * Strips all script tags\r
12490          * @param {Mixed} value The text from which to strip script tags\r
12491          * @return {String} The stripped text\r
12492          */\r
12493         stripScripts : function(v){\r
12494             return !v ? v : String(v).replace(stripScriptsRe, "");\r
12495         },\r
12496 \r
12497         /**\r
12498          * Simple format for a file size (xxx bytes, xxx KB, xxx MB)\r
12499          * @param {Number/String} size The numeric value to format\r
12500          * @return {String} The formatted file size\r
12501          */\r
12502         fileSize : function(size){\r
12503             if(size < 1024) {\r
12504                 return size + " bytes";\r
12505             } else if(size < 1048576) {\r
12506                 return (Math.round(((size*10) / 1024))/10) + " KB";\r
12507             } else {\r
12508                 return (Math.round(((size*10) / 1048576))/10) + " MB";\r
12509             }\r
12510         },\r
12511 \r
12512         /**\r
12513          * It does simple math for use in a template, for example:<pre><code>\r
12514          * var tpl = new Ext.Template('{value} * 10 = {value:math("* 10")}');\r
12515          * </code></pre>\r
12516          * @return {Function} A function that operates on the passed value.\r
12517          */\r
12518         math : function(){\r
12519             var fns = {};\r
12520             return function(v, a){\r
12521                 if(!fns[a]){\r
12522                     fns[a] = new Function('v', 'return v ' + a + ';');\r
12523                 }\r
12524                 return fns[a](v);\r
12525             }\r
12526         }(),\r
12527 \r
12528         /**\r
12529          * Rounds the passed number to the required decimal precision.\r
12530          * @param {Number/String} value The numeric value to round.\r
12531          * @param {Number} precision The number of decimal places to which to round the first parameter's value.\r
12532          * @return {Number} The rounded value.\r
12533          */\r
12534         round : function(value, precision) {\r
12535             var result = Number(value);\r
12536             if (typeof precision == 'number') {\r
12537                 precision = Math.pow(10, precision);\r
12538                 result = Math.round(value * precision) / precision;\r
12539             }\r
12540             return result;\r
12541         },\r
12542 \r
12543         /**\r
12544          * Formats the number according to the format string.\r
12545          * <div style="margin-left:40px">examples (123456.789):\r
12546          * <div style="margin-left:10px">\r
12547          * 0 - (123456) show only digits, no precision<br>\r
12548          * 0.00 - (123456.78) show only digits, 2 precision<br>\r
12549          * 0.0000 - (123456.7890) show only digits, 4 precision<br>\r
12550          * 0,000 - (123,456) show comma and digits, no precision<br>\r
12551          * 0,000.00 - (123,456.78) show comma and digits, 2 precision<br>\r
12552          * 0,0.00 - (123,456.78) shortcut method, show comma and digits, 2 precision<br>\r
12553          * To reverse the grouping (,) and decimal (.) for international numbers, add /i to the end.\r
12554          * For example: 0.000,00/i\r
12555          * </div></div>\r
12556          * @param {Number} v The number to format.\r
12557          * @param {String} format The way you would like to format this text.\r
12558          * @return {String} The formatted number.\r
12559          */\r
12560         number: function(v, format) {\r
12561             if(!format){\r
12562                         return v;\r
12563                     }\r
12564                     v = Ext.num(v, NaN);\r
12565             if (isNaN(v)){\r
12566                 return '';\r
12567             }\r
12568                     var comma = ',',\r
12569                         dec = '.',\r
12570                         i18n = false,\r
12571                         neg = v < 0;\r
12572                 \r
12573                     v = Math.abs(v);\r
12574                     if(format.substr(format.length - 2) == '/i'){\r
12575                         format = format.substr(0, format.length - 2);\r
12576                         i18n = true;\r
12577                         comma = '.';\r
12578                         dec = ',';\r
12579                     }\r
12580                 \r
12581                     var hasComma = format.indexOf(comma) != -1, \r
12582                         psplit = (i18n ? format.replace(/[^\d\,]/g, '') : format.replace(/[^\d\.]/g, '')).split(dec);\r
12583                 \r
12584                     if(1 < psplit.length){\r
12585                         v = v.toFixed(psplit[1].length);\r
12586                     }else if(2 < psplit.length){\r
12587                         throw ('NumberFormatException: invalid format, formats should have no more than 1 period: ' + format);\r
12588                     }else{\r
12589                         v = v.toFixed(0);\r
12590                     }\r
12591                 \r
12592                     var fnum = v.toString();\r
12593                     if(hasComma){\r
12594                         psplit = fnum.split('.');\r
12595                 \r
12596                         var cnum = psplit[0], parr = [], j = cnum.length, m = Math.floor(j / 3), n = cnum.length % 3 || 3;\r
12597                 \r
12598                         for(var i = 0; i < j; i += n){\r
12599                             if(i != 0){\r
12600                                 n = 3;\r
12601                             }\r
12602                             parr[parr.length] = cnum.substr(i, n);\r
12603                             m -= 1;\r
12604                         }\r
12605                         fnum = parr.join(comma);\r
12606                         if(psplit[1]){\r
12607                             fnum += dec + psplit[1];\r
12608                         }\r
12609                     }\r
12610                 \r
12611                     return (neg ? '-' : '') + format.replace(/[\d,?\.?]+/, fnum);\r
12612         },\r
12613 \r
12614         /**\r
12615          * Returns a number rendering function that can be reused to apply a number format multiple times efficiently\r
12616          * @param {String} format Any valid number format string for {@link #number}\r
12617          * @return {Function} The number formatting function\r
12618          */\r
12619         numberRenderer : function(format){\r
12620             return function(v){\r
12621                 return Ext.util.Format.number(v, format);\r
12622             };\r
12623         },\r
12624 \r
12625         /**\r
12626          * Selectively do a plural form of a word based on a numeric value. For example, in a template,\r
12627          * {commentCount:plural("Comment")}  would result in "1 Comment" if commentCount was 1 or would be "x Comments"\r
12628          * if the value is 0 or greater than 1.\r
12629          * @param {Number} value The value to compare against\r
12630          * @param {String} singular The singular form of the word\r
12631          * @param {String} plural (optional) The plural form of the word (defaults to the singular with an "s")\r
12632          */\r
12633         plural : function(v, s, p){\r
12634             return v +' ' + (v == 1 ? s : (p ? p : s+'s'));\r
12635         },\r
12636         \r
12637         /**\r
12638          * Converts newline characters to the HTML tag &lt;br/>\r
12639          * @param {String} The string value to format.\r
12640          * @return {String} The string with embedded &lt;br/> tags in place of newlines.\r
12641          */\r
12642         nl2br : function(v){\r
12643             return Ext.isEmpty(v) ? '' : v.replace(nl2brRe, '<br/>');\r
12644         }\r
12645     }\r
12646 }();\r
12647 /**
12648  * @class Ext.XTemplate
12649  * @extends Ext.Template
12650  * <p>A template class that supports advanced functionality like:<div class="mdetail-params"><ul>
12651  * <li>Autofilling arrays using templates and sub-templates</li>
12652  * <li>Conditional processing with basic comparison operators</li>
12653  * <li>Basic math function support</li>
12654  * <li>Execute arbitrary inline code with special built-in template variables</li>
12655  * <li>Custom member functions</li>
12656  * <li>Many special tags and built-in operators that aren't defined as part of
12657  * the API, but are supported in the templates that can be created</li>
12658  * </ul></div></p>
12659  * <p>XTemplate provides the templating mechanism built into:<div class="mdetail-params"><ul>
12660  * <li>{@link Ext.DataView}</li>
12661  * <li>{@link Ext.ListView}</li>
12662  * <li>{@link Ext.form.ComboBox}</li>
12663  * <li>{@link Ext.grid.TemplateColumn}</li>
12664  * <li>{@link Ext.grid.GroupingView}</li>
12665  * <li>{@link Ext.menu.Item}</li>
12666  * <li>{@link Ext.layout.MenuLayout}</li>
12667  * <li>{@link Ext.ColorPalette}</li>
12668  * </ul></div></p>
12669  * 
12670  * <p>For example usage {@link #XTemplate see the constructor}.</p>  
12671  *   
12672  * @constructor
12673  * The {@link Ext.Template#Template Ext.Template constructor} describes
12674  * the acceptable parameters to pass to the constructor. The following
12675  * examples demonstrate all of the supported features.</p>
12676  * 
12677  * <div class="mdetail-params"><ul>
12678  * 
12679  * <li><b><u>Sample Data</u></b> 
12680  * <div class="sub-desc">
12681  * <p>This is the data object used for reference in each code example:</p>
12682  * <pre><code>
12683 var data = {
12684     name: 'Jack Slocum',
12685     title: 'Lead Developer',
12686     company: 'Ext JS, LLC',
12687     email: 'jack@extjs.com',
12688     address: '4 Red Bulls Drive',
12689     city: 'Cleveland',
12690     state: 'Ohio',
12691     zip: '44102',
12692     drinks: ['Red Bull', 'Coffee', 'Water'],
12693     kids: [{
12694         name: 'Sara Grace',
12695         age:3
12696     },{
12697         name: 'Zachary',
12698         age:2
12699     },{
12700         name: 'John James',
12701         age:0
12702     }]
12703 };
12704  * </code></pre>
12705  * </div>
12706  * </li>
12707  * 
12708  * 
12709  * <li><b><u>Auto filling of arrays</u></b> 
12710  * <div class="sub-desc">
12711  * <p>The <b><tt>tpl</tt></b> tag and the <b><tt>for</tt></b> operator are used
12712  * to process the provided data object:
12713  * <ul>
12714  * <li>If the value specified in <tt>for</tt> is an array, it will auto-fill,
12715  * repeating the template block inside the <tt>tpl</tt> tag for each item in the
12716  * array.</li>
12717  * <li>If <tt>for="."</tt> is specified, the data object provided is examined.</li>
12718  * <li>While processing an array, the special variable <tt>{#}</tt>
12719  * will provide the current array index + 1 (starts at 1, not 0).</li>
12720  * </ul>
12721  * </p>
12722  * <pre><code>
12723 &lt;tpl <b>for</b>=".">...&lt;/tpl>       // loop through array at root node
12724 &lt;tpl <b>for</b>="foo">...&lt;/tpl>     // loop through array at foo node
12725 &lt;tpl <b>for</b>="foo.bar">...&lt;/tpl> // loop through array at foo.bar node
12726  * </code></pre>
12727  * Using the sample data above:
12728  * <pre><code>
12729 var tpl = new Ext.XTemplate(
12730     '&lt;p>Kids: ',
12731     '&lt;tpl <b>for</b>=".">',       // process the data.kids node
12732         '&lt;p>{#}. {name}&lt;/p>',  // use current array index to autonumber
12733     '&lt;/tpl>&lt;/p>'
12734 );
12735 tpl.overwrite(panel.body, data.kids); // pass the kids property of the data object
12736  * </code></pre>
12737  * <p>An example illustrating how the <b><tt>for</tt></b> property can be leveraged
12738  * to access specified members of the provided data object to populate the template:</p>
12739  * <pre><code>
12740 var tpl = new Ext.XTemplate(
12741     '&lt;p>Name: {name}&lt;/p>',
12742     '&lt;p>Title: {title}&lt;/p>',
12743     '&lt;p>Company: {company}&lt;/p>',
12744     '&lt;p>Kids: ',
12745     '&lt;tpl <b>for="kids"</b>>',     // interrogate the kids property within the data
12746         '&lt;p>{name}&lt;/p>',
12747     '&lt;/tpl>&lt;/p>'
12748 );
12749 tpl.overwrite(panel.body, data);  // pass the root node of the data object
12750  * </code></pre>
12751  * <p>Flat arrays that contain values (and not objects) can be auto-rendered
12752  * using the special <b><tt>{.}</tt></b> variable inside a loop.  This variable
12753  * will represent the value of the array at the current index:</p>
12754  * <pre><code>
12755 var tpl = new Ext.XTemplate(
12756     '&lt;p>{name}\&#39;s favorite beverages:&lt;/p>',
12757     '&lt;tpl for="drinks">',
12758        '&lt;div> - {.}&lt;/div>',
12759     '&lt;/tpl>'
12760 );
12761 tpl.overwrite(panel.body, data);
12762  * </code></pre>
12763  * <p>When processing a sub-template, for example while looping through a child array,
12764  * you can access the parent object's members via the <b><tt>parent</tt></b> object:</p>
12765  * <pre><code>
12766 var tpl = new Ext.XTemplate(
12767     '&lt;p>Name: {name}&lt;/p>',
12768     '&lt;p>Kids: ',
12769     '&lt;tpl for="kids">',
12770         '&lt;tpl if="age > 1">',
12771             '&lt;p>{name}&lt;/p>',
12772             '&lt;p>Dad: {<b>parent</b>.name}&lt;/p>',
12773         '&lt;/tpl>',
12774     '&lt;/tpl>&lt;/p>'
12775 );
12776 tpl.overwrite(panel.body, data);
12777  * </code></pre>
12778  * </div>
12779  * </li>
12780  * 
12781  * 
12782  * <li><b><u>Conditional processing with basic comparison operators</u></b> 
12783  * <div class="sub-desc">
12784  * <p>The <b><tt>tpl</tt></b> tag and the <b><tt>if</tt></b> operator are used
12785  * to provide conditional checks for deciding whether or not to render specific
12786  * parts of the template. Notes:<div class="sub-desc"><ul>
12787  * <li>Double quotes must be encoded if used within the conditional</li>
12788  * <li>There is no <tt>else</tt> operator &mdash; if needed, two opposite
12789  * <tt>if</tt> statements should be used.</li>
12790  * </ul></div>
12791  * <pre><code>
12792 &lt;tpl if="age &gt; 1 &amp;&amp; age &lt; 10">Child&lt;/tpl>
12793 &lt;tpl if="age >= 10 && age < 18">Teenager&lt;/tpl>
12794 &lt;tpl <b>if</b>="this.isGirl(name)">...&lt;/tpl>
12795 &lt;tpl <b>if</b>="id==\'download\'">...&lt;/tpl>
12796 &lt;tpl <b>if</b>="needsIcon">&lt;img src="{icon}" class="{iconCls}"/>&lt;/tpl>
12797 // no good:
12798 &lt;tpl if="name == "Jack"">Hello&lt;/tpl>
12799 // encode &#34; if it is part of the condition, e.g.
12800 &lt;tpl if="name == &#38;quot;Jack&#38;quot;">Hello&lt;/tpl>
12801  * </code></pre>
12802  * Using the sample data above:
12803  * <pre><code>
12804 var tpl = new Ext.XTemplate(
12805     '&lt;p>Name: {name}&lt;/p>',
12806     '&lt;p>Kids: ',
12807     '&lt;tpl for="kids">',
12808         '&lt;tpl if="age > 1">',
12809             '&lt;p>{name}&lt;/p>',
12810         '&lt;/tpl>',
12811     '&lt;/tpl>&lt;/p>'
12812 );
12813 tpl.overwrite(panel.body, data);
12814  * </code></pre>
12815  * </div>
12816  * </li>
12817  * 
12818  * 
12819  * <li><b><u>Basic math support</u></b> 
12820  * <div class="sub-desc">
12821  * <p>The following basic math operators may be applied directly on numeric
12822  * data values:</p><pre>
12823  * + - * /
12824  * </pre>
12825  * For example:
12826  * <pre><code>
12827 var tpl = new Ext.XTemplate(
12828     '&lt;p>Name: {name}&lt;/p>',
12829     '&lt;p>Kids: ',
12830     '&lt;tpl for="kids">',
12831         '&lt;tpl if="age &amp;gt; 1">',  // <-- Note that the &gt; is encoded
12832             '&lt;p>{#}: {name}&lt;/p>',  // <-- Auto-number each item
12833             '&lt;p>In 5 Years: {age+5}&lt;/p>',  // <-- Basic math
12834             '&lt;p>Dad: {parent.name}&lt;/p>',
12835         '&lt;/tpl>',
12836     '&lt;/tpl>&lt;/p>'
12837 );
12838 tpl.overwrite(panel.body, data);
12839 </code></pre>
12840  * </div>
12841  * </li>
12842  *
12843  * 
12844  * <li><b><u>Execute arbitrary inline code with special built-in template variables</u></b> 
12845  * <div class="sub-desc">
12846  * <p>Anything between <code>{[ ... ]}</code> is considered code to be executed
12847  * in the scope of the template. There are some special variables available in that code:
12848  * <ul>
12849  * <li><b><tt>values</tt></b>: The values in the current scope. If you are using
12850  * scope changing sub-templates, you can change what <tt>values</tt> is.</li>
12851  * <li><b><tt>parent</tt></b>: The scope (values) of the ancestor template.</li>
12852  * <li><b><tt>xindex</tt></b>: If you are in a looping template, the index of the
12853  * loop you are in (1-based).</li>
12854  * <li><b><tt>xcount</tt></b>: If you are in a looping template, the total length
12855  * of the array you are looping.</li>
12856  * <li><b><tt>fm</tt></b>: An alias for <tt>Ext.util.Format</tt>.</li>
12857  * </ul>
12858  * This example demonstrates basic row striping using an inline code block and the
12859  * <tt>xindex</tt> variable:</p>
12860  * <pre><code>
12861 var tpl = new Ext.XTemplate(
12862     '&lt;p>Name: {name}&lt;/p>',
12863     '&lt;p>Company: {[values.company.toUpperCase() + ", " + values.title]}&lt;/p>',
12864     '&lt;p>Kids: ',
12865     '&lt;tpl for="kids">',
12866        '&lt;div class="{[xindex % 2 === 0 ? "even" : "odd"]}">',
12867         '{name}',
12868         '&lt;/div>',
12869     '&lt;/tpl>&lt;/p>'
12870 );
12871 tpl.overwrite(panel.body, data);
12872  * </code></pre>
12873  * </div>
12874  * </li>
12875  * 
12876  * <li><b><u>Template member functions</u></b> 
12877  * <div class="sub-desc">
12878  * <p>One or more member functions can be specified in a configuration
12879  * object passed into the XTemplate constructor for more complex processing:</p>
12880  * <pre><code>
12881 var tpl = new Ext.XTemplate(
12882     '&lt;p>Name: {name}&lt;/p>',
12883     '&lt;p>Kids: ',
12884     '&lt;tpl for="kids">',
12885         '&lt;tpl if="this.isGirl(name)">',
12886             '&lt;p>Girl: {name} - {age}&lt;/p>',
12887         '&lt;/tpl>',
12888         // use opposite if statement to simulate 'else' processing:
12889         '&lt;tpl if="this.isGirl(name) == false">',
12890             '&lt;p>Boy: {name} - {age}&lt;/p>',
12891         '&lt;/tpl>',
12892         '&lt;tpl if="this.isBaby(age)">',
12893             '&lt;p>{name} is a baby!&lt;/p>',
12894         '&lt;/tpl>',
12895     '&lt;/tpl>&lt;/p>',
12896     {
12897         // XTemplate configuration:
12898         compiled: true,
12899         disableFormats: true,
12900         // member functions:
12901         isGirl: function(name){
12902             return name == 'Sara Grace';
12903         },
12904         isBaby: function(age){
12905             return age < 1;
12906         }
12907     }
12908 );
12909 tpl.overwrite(panel.body, data);
12910  * </code></pre>
12911  * </div>
12912  * </li>
12913  * 
12914  * </ul></div>
12915  * 
12916  * @param {Mixed} config
12917  */
12918 Ext.XTemplate = function(){
12919     Ext.XTemplate.superclass.constructor.apply(this, arguments);
12920
12921     var me = this,
12922         s = me.html,
12923         re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
12924         nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
12925         ifRe = /^<tpl\b[^>]*?if="(.*?)"/,
12926         execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
12927         m,
12928         id = 0,
12929         tpls = [],
12930         VALUES = 'values',
12931         PARENT = 'parent',
12932         XINDEX = 'xindex',
12933         XCOUNT = 'xcount',
12934         RETURN = 'return ',
12935         WITHVALUES = 'with(values){ ';
12936
12937     s = ['<tpl>', s, '</tpl>'].join('');
12938
12939     while((m = s.match(re))){
12940         var m2 = m[0].match(nameRe),
12941                         m3 = m[0].match(ifRe),
12942                 m4 = m[0].match(execRe),
12943                 exp = null,
12944                 fn = null,
12945                 exec = null,
12946                 name = m2 && m2[1] ? m2[1] : '';
12947
12948        if (m3) {
12949            exp = m3 && m3[1] ? m3[1] : null;
12950            if(exp){
12951                fn = new Function(VALUES, PARENT, XINDEX, XCOUNT, WITHVALUES + RETURN +(Ext.util.Format.htmlDecode(exp))+'; }');
12952            }
12953        }
12954        if (m4) {
12955            exp = m4 && m4[1] ? m4[1] : null;
12956            if(exp){
12957                exec = new Function(VALUES, PARENT, XINDEX, XCOUNT, WITHVALUES +(Ext.util.Format.htmlDecode(exp))+'; }');
12958            }
12959        }
12960        if(name){
12961            switch(name){
12962                case '.': name = new Function(VALUES, PARENT, WITHVALUES + RETURN + VALUES + '; }'); break;
12963                case '..': name = new Function(VALUES, PARENT, WITHVALUES + RETURN + PARENT + '; }'); break;
12964                default: name = new Function(VALUES, PARENT, WITHVALUES + RETURN + name + '; }');
12965            }
12966        }
12967        tpls.push({
12968             id: id,
12969             target: name,
12970             exec: exec,
12971             test: fn,
12972             body: m[1]||''
12973         });
12974        s = s.replace(m[0], '{xtpl'+ id + '}');
12975        ++id;
12976     }
12977         Ext.each(tpls, function(t) {
12978         me.compileTpl(t);
12979     });
12980     me.master = tpls[tpls.length-1];
12981     me.tpls = tpls;
12982 };
12983 Ext.extend(Ext.XTemplate, Ext.Template, {
12984     // private
12985     re : /\{([\w-\.\#]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?(\s?[\+\-\*\\]\s?[\d\.\+\-\*\\\(\)]+)?\}/g,
12986     // private
12987     codeRe : /\{\[((?:\\\]|.|\n)*?)\]\}/g,
12988
12989     // private
12990     applySubTemplate : function(id, values, parent, xindex, xcount){
12991         var me = this,
12992                 len,
12993                 t = me.tpls[id],
12994                 vs,
12995                 buf = [];
12996         if ((t.test && !t.test.call(me, values, parent, xindex, xcount)) ||
12997             (t.exec && t.exec.call(me, values, parent, xindex, xcount))) {
12998             return '';
12999         }
13000         vs = t.target ? t.target.call(me, values, parent) : values;
13001         len = vs.length;
13002         parent = t.target ? values : parent;
13003         if(t.target && Ext.isArray(vs)){
13004                 Ext.each(vs, function(v, i) {
13005                 buf[buf.length] = t.compiled.call(me, v, parent, i+1, len);
13006             });
13007             return buf.join('');
13008         }
13009         return t.compiled.call(me, vs, parent, xindex, xcount);
13010     },
13011
13012     // private
13013     compileTpl : function(tpl){
13014         var fm = Ext.util.Format,
13015                 useF = this.disableFormats !== true,
13016             sep = Ext.isGecko ? "+" : ",",
13017             body;
13018
13019         function fn(m, name, format, args, math){
13020             if(name.substr(0, 4) == 'xtpl'){
13021                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent, xindex, xcount)'+sep+"'";
13022             }
13023             var v;
13024             if(name === '.'){
13025                 v = 'values';
13026             }else if(name === '#'){
13027                 v = 'xindex';
13028             }else if(name.indexOf('.') != -1){
13029                 v = name;
13030             }else{
13031                 v = "values['" + name + "']";
13032             }
13033             if(math){
13034                 v = '(' + v + math + ')';
13035             }
13036             if (format && useF) {
13037                 args = args ? ',' + args : "";
13038                 if(format.substr(0, 5) != "this."){
13039                     format = "fm." + format + '(';
13040                 }else{
13041                     format = 'this.call("'+ format.substr(5) + '", ';
13042                     args = ", values";
13043                 }
13044             } else {
13045                 args= ''; format = "("+v+" === undefined ? '' : ";
13046             }
13047             return "'"+ sep + format + v + args + ")"+sep+"'";
13048         }
13049
13050         function codeFn(m, code){
13051             // Single quotes get escaped when the template is compiled, however we want to undo this when running code.
13052             return "'" + sep + '(' + code.replace(/\\'/g, "'") + ')' + sep + "'";
13053         }
13054
13055         // branched to use + in gecko and [].join() in others
13056         if(Ext.isGecko){
13057             body = "tpl.compiled = function(values, parent, xindex, xcount){ return '" +
13058                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn).replace(this.codeRe, codeFn) +
13059                     "';};";
13060         }else{
13061             body = ["tpl.compiled = function(values, parent, xindex, xcount){ return ['"];
13062             body.push(tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn).replace(this.codeRe, codeFn));
13063             body.push("'].join('');};");
13064             body = body.join('');
13065         }
13066         eval(body);
13067         return this;
13068     },
13069
13070     /**
13071      * Returns an HTML fragment of this template with the specified values applied.
13072      * @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'})
13073      * @return {String} The HTML fragment
13074      */
13075     applyTemplate : function(values){
13076         return this.master.compiled.call(this, values, {}, 1, 1);
13077     },
13078
13079     /**
13080      * Compile the template to a function for optimized performance.  Recommended if the template will be used frequently.
13081      * @return {Function} The compiled function
13082      */
13083     compile : function(){return this;}
13084
13085     /**
13086      * @property re
13087      * @hide
13088      */
13089     /**
13090      * @property disableFormats
13091      * @hide
13092      */
13093     /**
13094      * @method set
13095      * @hide
13096      */
13097
13098 });
13099 /**
13100  * Alias for {@link #applyTemplate}
13101  * Returns an HTML fragment of this template with the specified values applied.
13102  * @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'})
13103  * @return {String} The HTML fragment
13104  * @member Ext.XTemplate
13105  * @method apply
13106  */
13107 Ext.XTemplate.prototype.apply = Ext.XTemplate.prototype.applyTemplate;
13108
13109 /**
13110  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
13111  * @param {String/HTMLElement} el A DOM element or its id
13112  * @return {Ext.Template} The created template
13113  * @static
13114  */
13115 Ext.XTemplate.from = function(el){
13116     el = Ext.getDom(el);
13117     return new Ext.XTemplate(el.value || el.innerHTML);
13118 };/**\r
13119  * @class Ext.util.CSS\r
13120  * Utility class for manipulating CSS rules\r
13121  * @singleton\r
13122  */\r
13123 Ext.util.CSS = function(){\r
13124         var rules = null;\r
13125         var doc = document;\r
13126 \r
13127     var camelRe = /(-[a-z])/gi;\r
13128     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };\r
13129 \r
13130    return {\r
13131    /**\r
13132     * Creates a stylesheet from a text blob of rules.\r
13133     * These rules will be wrapped in a STYLE tag and appended to the HEAD of the document.\r
13134     * @param {String} cssText The text containing the css rules\r
13135     * @param {String} id An id to add to the stylesheet for later removal\r
13136     * @return {StyleSheet}\r
13137     */\r
13138    createStyleSheet : function(cssText, id){\r
13139        var ss;\r
13140        var head = doc.getElementsByTagName("head")[0];\r
13141        var rules = doc.createElement("style");\r
13142        rules.setAttribute("type", "text/css");\r
13143        if(id){\r
13144            rules.setAttribute("id", id);\r
13145        }\r
13146        if(Ext.isIE){\r
13147            head.appendChild(rules);\r
13148            ss = rules.styleSheet;\r
13149            ss.cssText = cssText;\r
13150        }else{\r
13151            try{\r
13152                 rules.appendChild(doc.createTextNode(cssText));\r
13153            }catch(e){\r
13154                rules.cssText = cssText;\r
13155            }\r
13156            head.appendChild(rules);\r
13157            ss = rules.styleSheet ? rules.styleSheet : (rules.sheet || doc.styleSheets[doc.styleSheets.length-1]);\r
13158        }\r
13159        this.cacheStyleSheet(ss);\r
13160        return ss;\r
13161    },\r
13162 \r
13163    /**\r
13164     * Removes a style or link tag by id\r
13165     * @param {String} id The id of the tag\r
13166     */\r
13167    removeStyleSheet : function(id){\r
13168        var existing = doc.getElementById(id);\r
13169        if(existing){\r
13170            existing.parentNode.removeChild(existing);\r
13171        }\r
13172    },\r
13173 \r
13174    /**\r
13175     * Dynamically swaps an existing stylesheet reference for a new one\r
13176     * @param {String} id The id of an existing link tag to remove\r
13177     * @param {String} url The href of the new stylesheet to include\r
13178     */\r
13179    swapStyleSheet : function(id, url){\r
13180        this.removeStyleSheet(id);\r
13181        var ss = doc.createElement("link");\r
13182        ss.setAttribute("rel", "stylesheet");\r
13183        ss.setAttribute("type", "text/css");\r
13184        ss.setAttribute("id", id);\r
13185        ss.setAttribute("href", url);\r
13186        doc.getElementsByTagName("head")[0].appendChild(ss);\r
13187    },\r
13188    \r
13189    /**\r
13190     * Refresh the rule cache if you have dynamically added stylesheets\r
13191     * @return {Object} An object (hash) of rules indexed by selector\r
13192     */\r
13193    refreshCache : function(){\r
13194        return this.getRules(true);\r
13195    },\r
13196 \r
13197    // private\r
13198    cacheStyleSheet : function(ss){\r
13199        if(!rules){\r
13200            rules = {};\r
13201        }\r
13202        try{// try catch for cross domain access issue\r
13203            var ssRules = ss.cssRules || ss.rules;\r
13204            for(var j = ssRules.length-1; j >= 0; --j){\r
13205                rules[ssRules[j].selectorText.toLowerCase()] = ssRules[j];\r
13206            }\r
13207        }catch(e){}\r
13208    },\r
13209    \r
13210    /**\r
13211     * Gets all css rules for the document\r
13212     * @param {Boolean} refreshCache true to refresh the internal cache\r
13213     * @return {Object} An object (hash) of rules indexed by selector\r
13214     */\r
13215    getRules : function(refreshCache){\r
13216                 if(rules === null || refreshCache){\r
13217                         rules = {};\r
13218                         var ds = doc.styleSheets;\r
13219                         for(var i =0, len = ds.length; i < len; i++){\r
13220                             try{\r
13221                         this.cacheStyleSheet(ds[i]);\r
13222                     }catch(e){} \r
13223                 }\r
13224                 }\r
13225                 return rules;\r
13226         },\r
13227         \r
13228         /**\r
13229     * Gets an an individual CSS rule by selector(s)\r
13230     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.\r
13231     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically\r
13232     * @return {CSSRule} The CSS rule or null if one is not found\r
13233     */\r
13234    getRule : function(selector, refreshCache){\r
13235                 var rs = this.getRules(refreshCache);\r
13236                 if(!Ext.isArray(selector)){\r
13237                     return rs[selector.toLowerCase()];\r
13238                 }\r
13239                 for(var i = 0; i < selector.length; i++){\r
13240                         if(rs[selector[i]]){\r
13241                                 return rs[selector[i].toLowerCase()];\r
13242                         }\r
13243                 }\r
13244                 return null;\r
13245         },\r
13246         \r
13247         \r
13248         /**\r
13249     * Updates a rule property\r
13250     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.\r
13251     * @param {String} property The css property\r
13252     * @param {String} value The new value for the property\r
13253     * @return {Boolean} true If a rule was found and updated\r
13254     */\r
13255    updateRule : function(selector, property, value){\r
13256                 if(!Ext.isArray(selector)){\r
13257                         var rule = this.getRule(selector);\r
13258                         if(rule){\r
13259                                 rule.style[property.replace(camelRe, camelFn)] = value;\r
13260                                 return true;\r
13261                         }\r
13262                 }else{\r
13263                         for(var i = 0; i < selector.length; i++){\r
13264                                 if(this.updateRule(selector[i], property, value)){\r
13265                                         return true;\r
13266                                 }\r
13267                         }\r
13268                 }\r
13269                 return false;\r
13270         }\r
13271    };   \r
13272 }();/**
13273  @class Ext.util.ClickRepeater
13274  @extends Ext.util.Observable
13275
13276  A wrapper class which can be applied to any element. Fires a "click" event while the
13277  mouse is pressed. The interval between firings may be specified in the config but
13278  defaults to 20 milliseconds.
13279
13280  Optionally, a CSS class may be applied to the element during the time it is pressed.
13281
13282  @cfg {Mixed} el The element to act as a button.
13283  @cfg {Number} delay The initial delay before the repeating event begins firing.
13284  Similar to an autorepeat key delay.
13285  @cfg {Number} interval The interval between firings of the "click" event. Default 20 ms.
13286  @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
13287  @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
13288            "interval" and "delay" are ignored.
13289  @cfg {Boolean} preventDefault True to prevent the default click event
13290  @cfg {Boolean} stopDefault True to stop the default click event
13291
13292  @history
13293     2007-02-02 jvs Original code contributed by Nige "Animal" White
13294     2007-02-02 jvs Renamed to ClickRepeater
13295     2007-02-03 jvs Modifications for FF Mac and Safari
13296
13297  @constructor
13298  @param {Mixed} el The element to listen on
13299  @param {Object} config
13300  */
13301 Ext.util.ClickRepeater = function(el, config)
13302 {
13303     this.el = Ext.get(el);
13304     this.el.unselectable();
13305
13306     Ext.apply(this, config);
13307
13308     this.addEvents(
13309     /**
13310      * @event mousedown
13311      * Fires when the mouse button is depressed.
13312      * @param {Ext.util.ClickRepeater} this
13313      */
13314         "mousedown",
13315     /**
13316      * @event click
13317      * Fires on a specified interval during the time the element is pressed.
13318      * @param {Ext.util.ClickRepeater} this
13319      */
13320         "click",
13321     /**
13322      * @event mouseup
13323      * Fires when the mouse key is released.
13324      * @param {Ext.util.ClickRepeater} this
13325      */
13326         "mouseup"
13327     );
13328
13329     if(!this.disabled){
13330         this.disabled = true;
13331         this.enable();
13332     }
13333
13334     // allow inline handler
13335     if(this.handler){
13336         this.on("click", this.handler,  this.scope || this);
13337     }
13338
13339     Ext.util.ClickRepeater.superclass.constructor.call(this);
13340 };
13341
13342 Ext.extend(Ext.util.ClickRepeater, Ext.util.Observable, {
13343     interval : 20,
13344     delay: 250,
13345     preventDefault : true,
13346     stopDefault : false,
13347     timer : 0,
13348
13349     /**
13350      * Enables the repeater and allows events to fire.
13351      */
13352     enable: function(){
13353         if(this.disabled){
13354             this.el.on('mousedown', this.handleMouseDown, this);
13355             if(this.preventDefault || this.stopDefault){
13356                 this.el.on('click', this.eventOptions, this);
13357             }
13358         }
13359         this.disabled = false;
13360     },
13361     
13362     /**
13363      * Disables the repeater and stops events from firing.
13364      */
13365     disable: function(/* private */ force){
13366         if(force || !this.disabled){
13367             clearTimeout(this.timer);
13368             if(this.pressClass){
13369                 this.el.removeClass(this.pressClass);
13370             }
13371             Ext.getDoc().un('mouseup', this.handleMouseUp, this);
13372             this.el.removeAllListeners();
13373         }
13374         this.disabled = true;
13375     },
13376     
13377     /**
13378      * Convenience function for setting disabled/enabled by boolean.
13379      * @param {Boolean} disabled
13380      */
13381     setDisabled: function(disabled){
13382         this[disabled ? 'disable' : 'enable']();    
13383     },
13384     
13385     eventOptions: function(e){
13386         if(this.preventDefault){
13387             e.preventDefault();
13388         }
13389         if(this.stopDefault){
13390             e.stopEvent();
13391         }       
13392     },
13393     
13394     // private
13395     destroy : function() {
13396         this.disable(true);
13397         Ext.destroy(this.el);
13398         this.purgeListeners();
13399     },
13400     
13401     // private
13402     handleMouseDown : function(){
13403         clearTimeout(this.timer);
13404         this.el.blur();
13405         if(this.pressClass){
13406             this.el.addClass(this.pressClass);
13407         }
13408         this.mousedownTime = new Date();
13409
13410         Ext.getDoc().on("mouseup", this.handleMouseUp, this);
13411         this.el.on("mouseout", this.handleMouseOut, this);
13412
13413         this.fireEvent("mousedown", this);
13414         this.fireEvent("click", this);
13415
13416 //      Do not honor delay or interval if acceleration wanted.
13417         if (this.accelerate) {
13418             this.delay = 400;
13419             }
13420         this.timer = this.click.defer(this.delay || this.interval, this);
13421     },
13422
13423     // private
13424     click : function(){
13425         this.fireEvent("click", this);
13426         this.timer = this.click.defer(this.accelerate ?
13427             this.easeOutExpo(this.mousedownTime.getElapsed(),
13428                 400,
13429                 -390,
13430                 12000) :
13431             this.interval, this);
13432     },
13433
13434     easeOutExpo : function (t, b, c, d) {
13435         return (t==d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b;
13436     },
13437
13438     // private
13439     handleMouseOut : function(){
13440         clearTimeout(this.timer);
13441         if(this.pressClass){
13442             this.el.removeClass(this.pressClass);
13443         }
13444         this.el.on("mouseover", this.handleMouseReturn, this);
13445     },
13446
13447     // private
13448     handleMouseReturn : function(){
13449         this.el.un("mouseover", this.handleMouseReturn, this);
13450         if(this.pressClass){
13451             this.el.addClass(this.pressClass);
13452         }
13453         this.click();
13454     },
13455
13456     // private
13457     handleMouseUp : function(){
13458         clearTimeout(this.timer);
13459         this.el.un("mouseover", this.handleMouseReturn, this);
13460         this.el.un("mouseout", this.handleMouseOut, this);
13461         Ext.getDoc().un("mouseup", this.handleMouseUp, this);
13462         this.el.removeClass(this.pressClass);
13463         this.fireEvent("mouseup", this);
13464     }
13465 });/**
13466  * @class Ext.KeyNav
13467  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
13468  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
13469  * way to implement custom navigation schemes for any UI component.</p>
13470  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
13471  * pageUp, pageDown, del, home, end.  Usage:</p>
13472  <pre><code>
13473 var nav = new Ext.KeyNav("my-element", {
13474     "left" : function(e){
13475         this.moveLeft(e.ctrlKey);
13476     },
13477     "right" : function(e){
13478         this.moveRight(e.ctrlKey);
13479     },
13480     "enter" : function(e){
13481         this.save();
13482     },
13483     scope : this
13484 });
13485 </code></pre>
13486  * @constructor
13487  * @param {Mixed} el The element to bind to
13488  * @param {Object} config The config
13489  */
13490 Ext.KeyNav = function(el, config){
13491     this.el = Ext.get(el);
13492     Ext.apply(this, config);
13493     if(!this.disabled){
13494         this.disabled = true;
13495         this.enable();
13496     }
13497 };
13498
13499 Ext.KeyNav.prototype = {
13500     /**
13501      * @cfg {Boolean} disabled
13502      * True to disable this KeyNav instance (defaults to false)
13503      */
13504     disabled : false,
13505     /**
13506      * @cfg {String} defaultEventAction
13507      * The method to call on the {@link Ext.EventObject} after this KeyNav intercepts a key.  Valid values are
13508      * {@link Ext.EventObject#stopEvent}, {@link Ext.EventObject#preventDefault} and
13509      * {@link Ext.EventObject#stopPropagation} (defaults to 'stopEvent')
13510      */
13511     defaultEventAction: "stopEvent",
13512     /**
13513      * @cfg {Boolean} forceKeyDown
13514      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
13515      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
13516      * handle keydown instead of keypress.
13517      */
13518     forceKeyDown : false,
13519
13520     // private
13521     relay : function(e){
13522         var k = e.getKey();
13523         var h = this.keyToHandler[k];
13524         if(h && this[h]){
13525             if(this.doRelay(e, this[h], h) !== true){
13526                 e[this.defaultEventAction]();
13527             }
13528         }
13529     },
13530
13531     // private
13532     doRelay : function(e, h, hname){
13533         return h.call(this.scope || this, e);
13534     },
13535
13536     // possible handlers
13537     enter : false,
13538     left : false,
13539     right : false,
13540     up : false,
13541     down : false,
13542     tab : false,
13543     esc : false,
13544     pageUp : false,
13545     pageDown : false,
13546     del : false,
13547     home : false,
13548     end : false,
13549
13550     // quick lookup hash
13551     keyToHandler : {
13552         37 : "left",
13553         39 : "right",
13554         38 : "up",
13555         40 : "down",
13556         33 : "pageUp",
13557         34 : "pageDown",
13558         46 : "del",
13559         36 : "home",
13560         35 : "end",
13561         13 : "enter",
13562         27 : "esc",
13563         9  : "tab"
13564     },
13565     
13566     stopKeyUp: function(e) {
13567         var k = e.getKey();
13568
13569         if (k >= 37 && k <= 40) {
13570             // *** bugfix - safari 2.x fires 2 keyup events on cursor keys
13571             // *** (note: this bugfix sacrifices the "keyup" event originating from keyNav elements in Safari 2)
13572             e.stopEvent();
13573         }
13574     },
13575     
13576     /**
13577      * Destroy this KeyNav (this is the same as calling disable).
13578      */
13579     destroy: function(){
13580         this.disable();    
13581     },
13582
13583         /**
13584          * Enable this KeyNav
13585          */
13586         enable: function() {
13587         if (this.disabled) {
13588             if (Ext.isSafari2) {
13589                 // call stopKeyUp() on "keyup" event
13590                 this.el.on('keyup', this.stopKeyUp, this);
13591             }
13592
13593             this.el.on(this.isKeydown()? 'keydown' : 'keypress', this.relay, this);
13594             this.disabled = false;
13595         }
13596     },
13597
13598         /**
13599          * Disable this KeyNav
13600          */
13601         disable: function() {
13602         if (!this.disabled) {
13603             if (Ext.isSafari2) {
13604                 // remove "keyup" event handler
13605                 this.el.un('keyup', this.stopKeyUp, this);
13606             }
13607
13608             this.el.un(this.isKeydown()? 'keydown' : 'keypress', this.relay, this);
13609             this.disabled = true;
13610         }
13611     },
13612     
13613     /**
13614      * Convenience function for setting disabled/enabled by boolean.
13615      * @param {Boolean} disabled
13616      */
13617     setDisabled : function(disabled){
13618         this[disabled ? "disable" : "enable"]();
13619     },
13620     
13621     // private
13622     isKeydown: function(){
13623         return this.forceKeyDown || Ext.EventManager.useKeydown;
13624     }
13625 };
13626 /**\r
13627  * @class Ext.KeyMap\r
13628  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.\r
13629  * The constructor accepts the same config object as defined by {@link #addBinding}.\r
13630  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key\r
13631  * combination it will call the function with this signature (if the match is a multi-key\r
13632  * combination the callback will still be called only once): (String key, Ext.EventObject e)\r
13633  * A KeyMap can also handle a string representation of keys.<br />\r
13634  * Usage:\r
13635  <pre><code>\r
13636 // map one key by key code\r
13637 var map = new Ext.KeyMap("my-element", {\r
13638     key: 13, // or Ext.EventObject.ENTER\r
13639     fn: myHandler,\r
13640     scope: myObject\r
13641 });\r
13642 \r
13643 // map multiple keys to one action by string\r
13644 var map = new Ext.KeyMap("my-element", {\r
13645     key: "a\r\n\t",\r
13646     fn: myHandler,\r
13647     scope: myObject\r
13648 });\r
13649 \r
13650 // map multiple keys to multiple actions by strings and array of codes\r
13651 var map = new Ext.KeyMap("my-element", [\r
13652     {\r
13653         key: [10,13],\r
13654         fn: function(){ alert("Return was pressed"); }\r
13655     }, {\r
13656         key: "abc",\r
13657         fn: function(){ alert('a, b or c was pressed'); }\r
13658     }, {\r
13659         key: "\t",\r
13660         ctrl:true,\r
13661         shift:true,\r
13662         fn: function(){ alert('Control + shift + tab was pressed.'); }\r
13663     }\r
13664 ]);\r
13665 </code></pre>\r
13666  * <b>Note: A KeyMap starts enabled</b>\r
13667  * @constructor\r
13668  * @param {Mixed} el The element to bind to\r
13669  * @param {Object} config The config (see {@link #addBinding})\r
13670  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")\r
13671  */\r
13672 Ext.KeyMap = function(el, config, eventName){\r
13673     this.el  = Ext.get(el);\r
13674     this.eventName = eventName || "keydown";\r
13675     this.bindings = [];\r
13676     if(config){\r
13677         this.addBinding(config);\r
13678     }\r
13679     this.enable();\r
13680 };\r
13681 \r
13682 Ext.KeyMap.prototype = {\r
13683     /**\r
13684      * True to stop the event from bubbling and prevent the default browser action if the\r
13685      * key was handled by the KeyMap (defaults to false)\r
13686      * @type Boolean\r
13687      */\r
13688     stopEvent : false,\r
13689 \r
13690     /**\r
13691      * Add a new binding to this KeyMap. The following config object properties are supported:\r
13692      * <pre>\r
13693 Property    Type             Description\r
13694 ----------  ---------------  ----------------------------------------------------------------------\r
13695 key         String/Array     A single keycode or an array of keycodes to handle\r
13696 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
13697 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
13698 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
13699 handler     Function         The function to call when KeyMap finds the expected key combination\r
13700 fn          Function         Alias of handler (for backwards-compatibility)\r
13701 scope       Object           The scope of the callback function\r
13702 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
13703 </pre>\r
13704      *\r
13705      * Usage:\r
13706      * <pre><code>\r
13707 // Create a KeyMap\r
13708 var map = new Ext.KeyMap(document, {\r
13709     key: Ext.EventObject.ENTER,\r
13710     fn: handleKey,\r
13711     scope: this\r
13712 });\r
13713 \r
13714 //Add a new binding to the existing KeyMap later\r
13715 map.addBinding({\r
13716     key: 'abc',\r
13717     shift: true,\r
13718     fn: handleKey,\r
13719     scope: this\r
13720 });\r
13721 </code></pre>\r
13722      * @param {Object/Array} config A single KeyMap config or an array of configs\r
13723      */\r
13724         addBinding : function(config){\r
13725         if(Ext.isArray(config)){\r
13726             Ext.each(config, function(c){\r
13727                 this.addBinding(c);\r
13728             }, this);\r
13729             return;\r
13730         }\r
13731         var keyCode = config.key,\r
13732             fn = config.fn || config.handler,\r
13733             scope = config.scope;\r
13734 \r
13735         if (config.stopEvent) {\r
13736             this.stopEvent = config.stopEvent;    \r
13737         }       \r
13738 \r
13739         if(typeof keyCode == "string"){\r
13740             var ks = [];\r
13741             var keyString = keyCode.toUpperCase();\r
13742             for(var j = 0, len = keyString.length; j < len; j++){\r
13743                 ks.push(keyString.charCodeAt(j));\r
13744             }\r
13745             keyCode = ks;\r
13746         }\r
13747         var keyArray = Ext.isArray(keyCode);\r
13748         \r
13749         var handler = function(e){\r
13750             if(this.checkModifiers(config, e)){\r
13751                 var k = e.getKey();\r
13752                 if(keyArray){\r
13753                     for(var i = 0, len = keyCode.length; i < len; i++){\r
13754                         if(keyCode[i] == k){\r
13755                           if(this.stopEvent){\r
13756                               e.stopEvent();\r
13757                           }\r
13758                           fn.call(scope || window, k, e);\r
13759                           return;\r
13760                         }\r
13761                     }\r
13762                 }else{\r
13763                     if(k == keyCode){\r
13764                         if(this.stopEvent){\r
13765                            e.stopEvent();\r
13766                         }\r
13767                         fn.call(scope || window, k, e);\r
13768                     }\r
13769                 }\r
13770             }\r
13771         };\r
13772         this.bindings.push(handler);\r
13773         },\r
13774     \r
13775     // private\r
13776     checkModifiers: function(config, e){\r
13777         var val, key, keys = ['shift', 'ctrl', 'alt'];\r
13778         for (var i = 0, len = keys.length; i < len; ++i){\r
13779             key = keys[i];\r
13780             val = config[key];\r
13781             if(!(val === undefined || (val === e[key + 'Key']))){\r
13782                 return false;\r
13783             }\r
13784         }\r
13785         return true;\r
13786     },\r
13787 \r
13788     /**\r
13789      * Shorthand for adding a single key listener\r
13790      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the\r
13791      * following options:\r
13792      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}\r
13793      * @param {Function} fn The function to call\r
13794      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the browser window.\r
13795      */\r
13796     on : function(key, fn, scope){\r
13797         var keyCode, shift, ctrl, alt;\r
13798         if(typeof key == "object" && !Ext.isArray(key)){\r
13799             keyCode = key.key;\r
13800             shift = key.shift;\r
13801             ctrl = key.ctrl;\r
13802             alt = key.alt;\r
13803         }else{\r
13804             keyCode = key;\r
13805         }\r
13806         this.addBinding({\r
13807             key: keyCode,\r
13808             shift: shift,\r
13809             ctrl: ctrl,\r
13810             alt: alt,\r
13811             fn: fn,\r
13812             scope: scope\r
13813         });\r
13814     },\r
13815 \r
13816     // private\r
13817     handleKeyDown : function(e){\r
13818             if(this.enabled){ //just in case\r
13819             var b = this.bindings;\r
13820             for(var i = 0, len = b.length; i < len; i++){\r
13821                 b[i].call(this, e);\r
13822             }\r
13823             }\r
13824         },\r
13825 \r
13826         /**\r
13827          * Returns true if this KeyMap is enabled\r
13828          * @return {Boolean}\r
13829          */\r
13830         isEnabled : function(){\r
13831             return this.enabled;\r
13832         },\r
13833 \r
13834         /**\r
13835          * Enables this KeyMap\r
13836          */\r
13837         enable: function(){\r
13838                 if(!this.enabled){\r
13839                     this.el.on(this.eventName, this.handleKeyDown, this);\r
13840                     this.enabled = true;\r
13841                 }\r
13842         },\r
13843 \r
13844         /**\r
13845          * Disable this KeyMap\r
13846          */\r
13847         disable: function(){\r
13848                 if(this.enabled){\r
13849                     this.el.removeListener(this.eventName, this.handleKeyDown, this);\r
13850                     this.enabled = false;\r
13851                 }\r
13852         },\r
13853     \r
13854     /**\r
13855      * Convenience function for setting disabled/enabled by boolean.\r
13856      * @param {Boolean} disabled\r
13857      */\r
13858     setDisabled : function(disabled){\r
13859         this[disabled ? "disable" : "enable"]();\r
13860     }\r
13861 };/**
13862  * @class Ext.util.TextMetrics
13863  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
13864  * wide, in pixels, a given block of text will be. Note that when measuring text, it should be plain text and
13865  * should not contain any HTML, otherwise it may not be measured correctly.
13866  * @singleton
13867  */
13868 Ext.util.TextMetrics = function(){
13869     var shared;
13870     return {
13871         /**
13872          * Measures the size of the specified text
13873          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
13874          * that can affect the size of the rendered text
13875          * @param {String} text The text to measure
13876          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
13877          * in order to accurately measure the text height
13878          * @return {Object} An object containing the text's size {width: (width), height: (height)}
13879          */
13880         measure : function(el, text, fixedWidth){
13881             if(!shared){
13882                 shared = Ext.util.TextMetrics.Instance(el, fixedWidth);
13883             }
13884             shared.bind(el);
13885             shared.setFixedWidth(fixedWidth || 'auto');
13886             return shared.getSize(text);
13887         },
13888
13889         /**
13890          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
13891          * the overhead of multiple calls to initialize the style properties on each measurement.
13892          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
13893          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
13894          * in order to accurately measure the text height
13895          * @return {Ext.util.TextMetrics.Instance} instance The new instance
13896          */
13897         createInstance : function(el, fixedWidth){
13898             return Ext.util.TextMetrics.Instance(el, fixedWidth);
13899         }
13900     };
13901 }();
13902
13903 Ext.util.TextMetrics.Instance = function(bindTo, fixedWidth){
13904     var ml = new Ext.Element(document.createElement('div'));
13905     document.body.appendChild(ml.dom);
13906     ml.position('absolute');
13907     ml.setLeftTop(-1000, -1000);
13908     ml.hide();
13909
13910     if(fixedWidth){
13911         ml.setWidth(fixedWidth);
13912     }
13913
13914     var instance = {
13915         /**
13916          * <p><b>Only available on the instance returned from {@link #createInstance}, <u>not</u> on the singleton.</b></p>
13917          * Returns the size of the specified text based on the internal element's style and width properties
13918          * @param {String} text The text to measure
13919          * @return {Object} An object containing the text's size {width: (width), height: (height)}
13920          */
13921         getSize : function(text){
13922             ml.update(text);
13923             var s = ml.getSize();
13924             ml.update('');
13925             return s;
13926         },
13927
13928         /**
13929          * <p><b>Only available on the instance returned from {@link #createInstance}, <u>not</u> on the singleton.</b></p>
13930          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
13931          * that can affect the size of the rendered text
13932          * @param {String/HTMLElement} el The element, dom node or id
13933          */
13934         bind : function(el){
13935             ml.setStyle(
13936                 Ext.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height', 'text-transform', 'letter-spacing')
13937             );
13938         },
13939
13940         /**
13941          * <p><b>Only available on the instance returned from {@link #createInstance}, <u>not</u> on the singleton.</b></p>
13942          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
13943          * to set a fixed width in order to accurately measure the text height.
13944          * @param {Number} width The width to set on the element
13945          */
13946         setFixedWidth : function(width){
13947             ml.setWidth(width);
13948         },
13949
13950         /**
13951          * <p><b>Only available on the instance returned from {@link #createInstance}, <u>not</u> on the singleton.</b></p>
13952          * Returns the measured width of the specified text
13953          * @param {String} text The text to measure
13954          * @return {Number} width The width in pixels
13955          */
13956         getWidth : function(text){
13957             ml.dom.style.width = 'auto';
13958             return this.getSize(text).width;
13959         },
13960
13961         /**
13962          * <p><b>Only available on the instance returned from {@link #createInstance}, <u>not</u> on the singleton.</b></p>
13963          * Returns the measured height of the specified text.  For multiline text, be sure to call
13964          * {@link #setFixedWidth} if necessary.
13965          * @param {String} text The text to measure
13966          * @return {Number} height The height in pixels
13967          */
13968         getHeight : function(text){
13969             return this.getSize(text).height;
13970         }
13971     };
13972
13973     instance.bind(bindTo);
13974
13975     return instance;
13976 };
13977
13978 Ext.Element.addMethods({
13979     /**
13980      * Returns the width in pixels of the passed text, or the width of the text in this Element.
13981      * @param {String} text The text to measure. Defaults to the innerHTML of the element.
13982      * @param {Number} min (Optional) The minumum value to return.
13983      * @param {Number} max (Optional) The maximum value to return.
13984      * @return {Number} The text width in pixels.
13985      * @member Ext.Element getTextWidth
13986      */
13987     getTextWidth : function(text, min, max){
13988         return (Ext.util.TextMetrics.measure(this.dom, Ext.value(text, this.dom.innerHTML, true)).width).constrain(min || 0, max || 1000000);
13989     }
13990 });
13991 /**\r
13992  * @class Ext.util.Cookies\r
13993  * Utility class for managing and interacting with cookies.\r
13994  * @singleton\r
13995  */\r
13996 Ext.util.Cookies = {\r
13997     /**\r
13998      * Create a cookie with the specified name and value. Additional settings\r
13999      * for the cookie may be optionally specified (for example: expiration,\r
14000      * access restriction, SSL).\r
14001      * @param {String} name The name of the cookie to set. \r
14002      * @param {Mixed} value The value to set for the cookie.\r
14003      * @param {Object} expires (Optional) Specify an expiration date the\r
14004      * cookie is to persist until.  Note that the specified Date object will\r
14005      * be converted to Greenwich Mean Time (GMT). \r
14006      * @param {String} path (Optional) Setting a path on the cookie restricts\r
14007      * access to pages that match that path. Defaults to all pages (<tt>'/'</tt>). \r
14008      * @param {String} domain (Optional) Setting a domain restricts access to\r
14009      * pages on a given domain (typically used to allow cookie access across\r
14010      * subdomains). For example, "extjs.com" will create a cookie that can be\r
14011      * accessed from any subdomain of extjs.com, including www.extjs.com,\r
14012      * support.extjs.com, etc.\r
14013      * @param {Boolean} secure (Optional) Specify true to indicate that the cookie\r
14014      * should only be accessible via SSL on a page using the HTTPS protocol.\r
14015      * Defaults to <tt>false</tt>. Note that this will only work if the page\r
14016      * calling this code uses the HTTPS protocol, otherwise the cookie will be\r
14017      * created with default options.\r
14018      */\r
14019     set : function(name, value){\r
14020         var argv = arguments;\r
14021         var argc = arguments.length;\r
14022         var expires = (argc > 2) ? argv[2] : null;\r
14023         var path = (argc > 3) ? argv[3] : '/';\r
14024         var domain = (argc > 4) ? argv[4] : null;\r
14025         var secure = (argc > 5) ? argv[5] : false;\r
14026         document.cookie = name + "=" + escape(value) + ((expires === null) ? "" : ("; expires=" + expires.toGMTString())) + ((path === null) ? "" : ("; path=" + path)) + ((domain === null) ? "" : ("; domain=" + domain)) + ((secure === true) ? "; secure" : "");\r
14027     },\r
14028 \r
14029     /**\r
14030      * Retrieves cookies that are accessible by the current page. If a cookie\r
14031      * does not exist, <code>get()</code> returns <tt>null</tt>.  The following\r
14032      * example retrieves the cookie called "valid" and stores the String value\r
14033      * in the variable <tt>validStatus</tt>.\r
14034      * <pre><code>\r
14035      * var validStatus = Ext.util.Cookies.get("valid");\r
14036      * </code></pre>\r
14037      * @param {String} name The name of the cookie to get\r
14038      * @return {Mixed} Returns the cookie value for the specified name;\r
14039      * null if the cookie name does not exist.\r
14040      */\r
14041     get : function(name){\r
14042         var arg = name + "=";\r
14043         var alen = arg.length;\r
14044         var clen = document.cookie.length;\r
14045         var i = 0;\r
14046         var j = 0;\r
14047         while(i < clen){\r
14048             j = i + alen;\r
14049             if(document.cookie.substring(i, j) == arg){\r
14050                 return Ext.util.Cookies.getCookieVal(j);\r
14051             }\r
14052             i = document.cookie.indexOf(" ", i) + 1;\r
14053             if(i === 0){\r
14054                 break;\r
14055             }\r
14056         }\r
14057         return null;\r
14058     },\r
14059 \r
14060     /**\r
14061      * Removes a cookie with the provided name from the browser\r
14062      * if found by setting its expiration date to sometime in the past. \r
14063      * @param {String} name The name of the cookie to remove\r
14064      */\r
14065     clear : function(name){\r
14066         if(Ext.util.Cookies.get(name)){\r
14067             document.cookie = name + "=" + "; expires=Thu, 01-Jan-70 00:00:01 GMT";\r
14068         }\r
14069     },\r
14070     /**\r
14071      * @private\r
14072      */\r
14073     getCookieVal : function(offset){\r
14074         var endstr = document.cookie.indexOf(";", offset);\r
14075         if(endstr == -1){\r
14076             endstr = document.cookie.length;\r
14077         }\r
14078         return unescape(document.cookie.substring(offset, endstr));\r
14079     }\r
14080 };/**
14081  * Framework-wide error-handler.  Developers can override this method to provide
14082  * custom exception-handling.  Framework errors will often extend from the base
14083  * Ext.Error class.
14084  * @param {Object/Error} e The thrown exception object.
14085  */
14086 Ext.handleError = function(e) {
14087     throw e;
14088 };
14089
14090 /**
14091  * @class Ext.Error
14092  * @extends Error
14093  * <p>A base error class. Future implementations are intended to provide more
14094  * robust error handling throughout the framework (<b>in the debug build only</b>)
14095  * to check for common errors and problems. The messages issued by this class
14096  * will aid error checking. Error checks will be automatically removed in the
14097  * production build so that performance is not negatively impacted.</p>
14098  * <p>Some sample messages currently implemented:</p><pre>
14099 "DataProxy attempted to execute an API-action but found an undefined
14100 url / function. Please review your Proxy url/api-configuration."
14101  * </pre><pre>
14102 "Could not locate your "root" property in your server response.
14103 Please review your JsonReader config to ensure the config-property
14104 "root" matches the property your server-response.  See the JsonReader
14105 docs for additional assistance."
14106  * </pre>
14107  * <p>An example of the code used for generating error messages:</p><pre><code>
14108 try {
14109     generateError({
14110         foo: 'bar'
14111     });
14112 }
14113 catch (e) {
14114     console.error(e);
14115 }
14116 function generateError(data) {
14117     throw new Ext.Error('foo-error', data);
14118 }
14119  * </code></pre>
14120  * @param {String} message
14121  */
14122 Ext.Error = function(message) {
14123     // Try to read the message from Ext.Error.lang
14124     this.message = (this.lang[message]) ? this.lang[message] : message;
14125 }
14126 Ext.Error.prototype = new Error();
14127 Ext.apply(Ext.Error.prototype, {
14128     // protected.  Extensions place their error-strings here.
14129     lang: {},
14130
14131     name: 'Ext.Error',
14132     /**
14133      * getName
14134      * @return {String}
14135      */
14136     getName : function() {
14137         return this.name;
14138     },
14139     /**
14140      * getMessage
14141      * @return {String}
14142      */
14143     getMessage : function() {
14144         return this.message;
14145     },
14146     /**
14147      * toJson
14148      * @return {String}
14149      */
14150     toJson : function() {
14151         return Ext.encode(this);
14152     }
14153 });
14154