Upgrade to ExtJS 3.2.0 - Released 03/30/2010
[extjs.git] / pkgs / ext-foundation-debug.js
1 /*!
2  * Ext JS Library 3.2.0
3  * Copyright(c) 2006-2010 Ext JS, Inc.
4  * licensing@extjs.com
5  * http://www.extjs.com/license
6  */
7 /**
8  * @class Ext.DomHelper
9  * <p>The DomHelper class provides a layer of abstraction from DOM and transparently supports creating
10  * elements via DOM or using HTML fragments. It also has the ability to create HTML fragment templates
11  * from your DOM building code.</p>
12  *
13  * <p><b><u>DomHelper element specification object</u></b></p>
14  * <p>A specification object is used when creating elements. Attributes of this object
15  * are assumed to be element attributes, except for 4 special attributes:
16  * <div class="mdetail-params"><ul>
17  * <li><b><tt>tag</tt></b> : <div class="sub-desc">The tag name of the element</div></li>
18  * <li><b><tt>children</tt></b> : or <tt>cn</tt><div class="sub-desc">An array of the
19  * same kind of element definition objects to be created and appended. These can be nested
20  * as deep as you want.</div></li>
21  * <li><b><tt>cls</tt></b> : <div class="sub-desc">The class attribute of the element.
22  * This will end up being either the "class" attribute on a HTML fragment or className
23  * for a DOM node, depending on whether DomHelper is using fragments or DOM.</div></li>
24  * <li><b><tt>html</tt></b> : <div class="sub-desc">The innerHTML for the element</div></li>
25  * </ul></div></p>
26  *
27  * <p><b><u>Insertion methods</u></b></p>
28  * <p>Commonly used insertion methods:
29  * <div class="mdetail-params"><ul>
30  * <li><b><tt>{@link #append}</tt></b> : <div class="sub-desc"></div></li>
31  * <li><b><tt>{@link #insertBefore}</tt></b> : <div class="sub-desc"></div></li>
32  * <li><b><tt>{@link #insertAfter}</tt></b> : <div class="sub-desc"></div></li>
33  * <li><b><tt>{@link #overwrite}</tt></b> : <div class="sub-desc"></div></li>
34  * <li><b><tt>{@link #createTemplate}</tt></b> : <div class="sub-desc"></div></li>
35  * <li><b><tt>{@link #insertHtml}</tt></b> : <div class="sub-desc"></div></li>
36  * </ul></div></p>
37  *
38  * <p><b><u>Example</u></b></p>
39  * <p>This is an example, where an unordered list with 3 children items is appended to an existing
40  * element with id <tt>'my-div'</tt>:<br>
41  <pre><code>
42 var dh = Ext.DomHelper; // create shorthand alias
43 // specification object
44 var spec = {
45     id: 'my-ul',
46     tag: 'ul',
47     cls: 'my-list',
48     // append children after creating
49     children: [     // may also specify 'cn' instead of 'children'
50         {tag: 'li', id: 'item0', html: 'List Item 0'},
51         {tag: 'li', id: 'item1', html: 'List Item 1'},
52         {tag: 'li', id: 'item2', html: 'List Item 2'}
53     ]
54 };
55 var list = dh.append(
56     'my-div', // the context element 'my-div' can either be the id or the actual node
57     spec      // the specification object
58 );
59  </code></pre></p>
60  * <p>Element creation specification parameters in this class may also be passed as an Array of
61  * specification objects. This can be used to insert multiple sibling nodes into an existing
62  * container very efficiently. For example, to add more list items to the example above:<pre><code>
63 dh.append('my-ul', [
64     {tag: 'li', id: 'item3', html: 'List Item 3'},
65     {tag: 'li', id: 'item4', html: 'List Item 4'}
66 ]);
67  * </code></pre></p>
68  *
69  * <p><b><u>Templating</u></b></p>
70  * <p>The real power is in the built-in templating. Instead of creating or appending any elements,
71  * <tt>{@link #createTemplate}</tt> returns a Template object which can be used over and over to
72  * insert new elements. Revisiting the example above, we could utilize templating this time:
73  * <pre><code>
74 // create the node
75 var list = dh.append('my-div', {tag: 'ul', cls: 'my-list'});
76 // get template
77 var tpl = dh.createTemplate({tag: 'li', id: 'item{0}', html: 'List Item {0}'});
78
79 for(var i = 0; i < 5, i++){
80     tpl.append(list, [i]); // use template to append to the actual node
81 }
82  * </code></pre></p>
83  * <p>An example using a template:<pre><code>
84 var html = '<a id="{0}" href="{1}" class="nav">{2}</a>';
85
86 var tpl = new Ext.DomHelper.createTemplate(html);
87 tpl.append('blog-roll', ['link1', 'http://www.jackslocum.com/', "Jack&#39;s Site"]);
88 tpl.append('blog-roll', ['link2', 'http://www.dustindiaz.com/', "Dustin&#39;s Site"]);
89  * </code></pre></p>
90  *
91  * <p>The same example using named parameters:<pre><code>
92 var html = '<a id="{id}" href="{url}" class="nav">{text}</a>';
93
94 var tpl = new Ext.DomHelper.createTemplate(html);
95 tpl.append('blog-roll', {
96     id: 'link1',
97     url: 'http://www.jackslocum.com/',
98     text: "Jack&#39;s Site"
99 });
100 tpl.append('blog-roll', {
101     id: 'link2',
102     url: 'http://www.dustindiaz.com/',
103     text: "Dustin&#39;s Site"
104 });
105  * </code></pre></p>
106  *
107  * <p><b><u>Compiling Templates</u></b></p>
108  * <p>Templates are applied using regular expressions. The performance is great, but if
109  * you are adding a bunch of DOM elements using the same template, you can increase
110  * performance even further by {@link Ext.Template#compile "compiling"} the template.
111  * The way "{@link Ext.Template#compile compile()}" works is the template is parsed and
112  * broken up at the different variable points and a dynamic function is created and eval'ed.
113  * The generated function performs string concatenation of these parts and the passed
114  * variables instead of using regular expressions.
115  * <pre><code>
116 var html = '<a id="{id}" href="{url}" class="nav">{text}</a>';
117
118 var tpl = new Ext.DomHelper.createTemplate(html);
119 tpl.compile();
120
121 //... use template like normal
122  * </code></pre></p>
123  *
124  * <p><b><u>Performance Boost</u></b></p>
125  * <p>DomHelper will transparently create HTML fragments when it can. Using HTML fragments instead
126  * of DOM can significantly boost performance.</p>
127  * <p>Element creation specification parameters may also be strings. If {@link #useDom} is <tt>false</tt>,
128  * then the string is used as innerHTML. If {@link #useDom} is <tt>true</tt>, a string specification
129  * results in the creation of a text node. Usage:</p>
130  * <pre><code>
131 Ext.DomHelper.useDom = true; // force it to use DOM; reduces performance
132  * </code></pre>
133  * @singleton
134  */
135 Ext.DomHelper = function(){
136     var tempTableEl = null,
137         emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i,
138         tableRe = /^table|tbody|tr|td$/i,
139         pub,
140         // kill repeat to save bytes
141         afterbegin = 'afterbegin',
142         afterend = 'afterend',
143         beforebegin = 'beforebegin',
144         beforeend = 'beforeend',
145         ts = '<table>',
146         te = '</table>',
147         tbs = ts+'<tbody>',
148         tbe = '</tbody>'+te,
149         trs = tbs + '<tr>',
150         tre = '</tr>'+tbe;
151
152     // private
153     function doInsert(el, o, returnElement, pos, sibling, append){
154         var newNode = pub.insertHtml(pos, Ext.getDom(el), createHtml(o));
155         return returnElement ? Ext.get(newNode, true) : newNode;
156     }
157
158     // build as innerHTML where available
159     function createHtml(o){
160         var b = '',
161             attr,
162             val,
163             key,
164             keyVal,
165             cn;
166
167         if(Ext.isString(o)){
168             b = o;
169         } else if (Ext.isArray(o)) {
170             for (var i=0; i < o.length; i++) {
171                 if(o[i]) {
172                     b += createHtml(o[i]);
173                 }
174             };
175         } else {
176             b += '<' + (o.tag = o.tag || 'div');
177             Ext.iterate(o, function(attr, val){
178                 if(!/tag|children|cn|html$/i.test(attr)){
179                     if (Ext.isObject(val)) {
180                         b += ' ' + attr + '="';
181                         Ext.iterate(val, function(key, keyVal){
182                             b += key + ':' + keyVal + ';';
183                         });
184                         b += '"';
185                     }else{
186                         b += ' ' + ({cls : 'class', htmlFor : 'for'}[attr] || attr) + '="' + val + '"';
187                     }
188                 }
189             });
190             // Now either just close the tag or try to add children and close the tag.
191             if (emptyTags.test(o.tag)) {
192                 b += '/>';
193             } else {
194                 b += '>';
195                 if ((cn = o.children || o.cn)) {
196                     b += createHtml(cn);
197                 } else if(o.html){
198                     b += o.html;
199                 }
200                 b += '</' + o.tag + '>';
201             }
202         }
203         return b;
204     }
205
206     function ieTable(depth, s, h, e){
207         tempTableEl.innerHTML = [s, h, e].join('');
208         var i = -1,
209             el = tempTableEl,
210             ns;
211         while(++i < depth){
212             el = el.firstChild;
213         }
214 //      If the result is multiple siblings, then encapsulate them into one fragment.
215         if(ns = el.nextSibling){
216             var df = document.createDocumentFragment();
217             while(el){
218                 ns = el.nextSibling;
219                 df.appendChild(el);
220                 el = ns;
221             }
222             el = df;
223         }
224         return el;
225     }
226
227     /**
228      * @ignore
229      * Nasty code for IE's broken table implementation
230      */
231     function insertIntoTable(tag, where, el, html) {
232         var node,
233             before;
234
235         tempTableEl = tempTableEl || document.createElement('div');
236
237         if(tag == 'td' && (where == afterbegin || where == beforeend) ||
238            !/td|tr|tbody/i.test(tag) && (where == beforebegin || where == afterend)) {
239             return;
240         }
241         before = where == beforebegin ? el :
242                  where == afterend ? el.nextSibling :
243                  where == afterbegin ? el.firstChild : null;
244
245         if (where == beforebegin || where == afterend) {
246             el = el.parentNode;
247         }
248
249         if (tag == 'td' || (tag == 'tr' && (where == beforeend || where == afterbegin))) {
250             node = ieTable(4, trs, html, tre);
251         } else if ((tag == 'tbody' && (where == beforeend || where == afterbegin)) ||
252                    (tag == 'tr' && (where == beforebegin || where == afterend))) {
253             node = ieTable(3, tbs, html, tbe);
254         } else {
255             node = ieTable(2, ts, html, te);
256         }
257         el.insertBefore(node, before);
258         return node;
259     }
260
261
262     pub = {
263         /**
264          * Returns the markup for the passed Element(s) config.
265          * @param {Object} o The DOM object spec (and children)
266          * @return {String}
267          */
268         markup : function(o){
269             return createHtml(o);
270         },
271         
272         /**
273          * Applies a style specification to an element.
274          * @param {String/HTMLElement} el The element to apply styles to
275          * @param {String/Object/Function} styles A style specification string e.g. 'width:100px', or object in the form {width:'100px'}, or
276          * a function which returns such a specification.
277          */
278         applyStyles : function(el, styles){
279             if(styles){
280                 var i = 0,
281                     len,
282                     style;
283
284                 el = Ext.fly(el);
285                 if(Ext.isFunction(styles)){
286                     styles = styles.call();
287                 }
288                 if(Ext.isString(styles)){
289                     styles = styles.trim().split(/\s*(?::|;)\s*/);
290                     for(len = styles.length; i < len;){
291                         el.setStyle(styles[i++], styles[i++]);
292                     }
293                 }else if (Ext.isObject(styles)){
294                     el.setStyle(styles);
295                 }
296             }
297         },
298
299         /**
300          * Inserts an HTML fragment into the DOM.
301          * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
302          * @param {HTMLElement} el The context element
303          * @param {String} html The HTML fragment
304          * @return {HTMLElement} The new node
305          */
306         insertHtml : function(where, el, html){
307             var hash = {},
308                 hashVal,
309                 setStart,
310                 range,
311                 frag,
312                 rangeEl,
313                 rs;
314
315             where = where.toLowerCase();
316             // add these here because they are used in both branches of the condition.
317             hash[beforebegin] = ['BeforeBegin', 'previousSibling'];
318             hash[afterend] = ['AfterEnd', 'nextSibling'];
319
320             if (el.insertAdjacentHTML) {
321                 if(tableRe.test(el.tagName) && (rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html))){
322                     return rs;
323                 }
324                 // add these two to the hash.
325                 hash[afterbegin] = ['AfterBegin', 'firstChild'];
326                 hash[beforeend] = ['BeforeEnd', 'lastChild'];
327                 if ((hashVal = hash[where])) {
328                     el.insertAdjacentHTML(hashVal[0], html);
329                     return el[hashVal[1]];
330                 }
331             } else {
332                 range = el.ownerDocument.createRange();
333                 setStart = 'setStart' + (/end/i.test(where) ? 'After' : 'Before');
334                 if (hash[where]) {
335                     range[setStart](el);
336                     frag = range.createContextualFragment(html);
337                     el.parentNode.insertBefore(frag, where == beforebegin ? el : el.nextSibling);
338                     return el[(where == beforebegin ? 'previous' : 'next') + 'Sibling'];
339                 } else {
340                     rangeEl = (where == afterbegin ? 'first' : 'last') + 'Child';
341                     if (el.firstChild) {
342                         range[setStart](el[rangeEl]);
343                         frag = range.createContextualFragment(html);
344                         if(where == afterbegin){
345                             el.insertBefore(frag, el.firstChild);
346                         }else{
347                             el.appendChild(frag);
348                         }
349                     } else {
350                         el.innerHTML = html;
351                     }
352                     return el[rangeEl];
353                 }
354             }
355             throw 'Illegal insertion point -> "' + where + '"';
356         },
357
358         /**
359          * Creates new DOM element(s) and inserts them before el.
360          * @param {Mixed} el The context element
361          * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
362          * @param {Boolean} returnElement (optional) true to return a Ext.Element
363          * @return {HTMLElement/Ext.Element} The new node
364          */
365         insertBefore : function(el, o, returnElement){
366             return doInsert(el, o, returnElement, beforebegin);
367         },
368
369         /**
370          * Creates new DOM element(s) and inserts them after el.
371          * @param {Mixed} el The context element
372          * @param {Object} o The DOM object spec (and children)
373          * @param {Boolean} returnElement (optional) true to return a Ext.Element
374          * @return {HTMLElement/Ext.Element} The new node
375          */
376         insertAfter : function(el, o, returnElement){
377             return doInsert(el, o, returnElement, afterend, 'nextSibling');
378         },
379
380         /**
381          * Creates new DOM element(s) and inserts them as the first child of el.
382          * @param {Mixed} el The context element
383          * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
384          * @param {Boolean} returnElement (optional) true to return a Ext.Element
385          * @return {HTMLElement/Ext.Element} The new node
386          */
387         insertFirst : function(el, o, returnElement){
388             return doInsert(el, o, returnElement, afterbegin, 'firstChild');
389         },
390
391         /**
392          * Creates new DOM element(s) and appends them to el.
393          * @param {Mixed} el The context element
394          * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
395          * @param {Boolean} returnElement (optional) true to return a Ext.Element
396          * @return {HTMLElement/Ext.Element} The new node
397          */
398         append : function(el, o, returnElement){
399             return doInsert(el, o, returnElement, beforeend, '', true);
400         },
401
402         /**
403          * Creates new DOM element(s) and overwrites the contents of el with them.
404          * @param {Mixed} el The context element
405          * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
406          * @param {Boolean} returnElement (optional) true to return a Ext.Element
407          * @return {HTMLElement/Ext.Element} The new node
408          */
409         overwrite : function(el, o, returnElement){
410             el = Ext.getDom(el);
411             el.innerHTML = createHtml(o);
412             return returnElement ? Ext.get(el.firstChild) : el.firstChild;
413         },
414
415         createHtml : createHtml
416     };
417     return pub;
418 }();/**
419  * @class Ext.DomHelper
420  */
421 Ext.apply(Ext.DomHelper,
422 function(){
423         var pub,
424                 afterbegin = 'afterbegin',
425         afterend = 'afterend',
426         beforebegin = 'beforebegin',
427         beforeend = 'beforeend';
428
429         // private
430     function doInsert(el, o, returnElement, pos, sibling, append){
431         el = Ext.getDom(el);
432         var newNode;
433         if (pub.useDom) {
434             newNode = createDom(o, null);
435             if (append) {
436                     el.appendChild(newNode);
437             } else {
438                         (sibling == 'firstChild' ? el : el.parentNode).insertBefore(newNode, el[sibling] || el);
439             }
440         } else {
441             newNode = Ext.DomHelper.insertHtml(pos, el, Ext.DomHelper.createHtml(o));
442         }
443         return returnElement ? Ext.get(newNode, true) : newNode;
444     }
445
446         // build as dom
447     /** @ignore */
448     function createDom(o, parentNode){
449         var el,
450                 doc = document,
451                 useSet,
452                 attr,
453                 val,
454                 cn;
455
456         if (Ext.isArray(o)) {                       // Allow Arrays of siblings to be inserted
457             el = doc.createDocumentFragment(); // in one shot using a DocumentFragment
458                 Ext.each(o, function(v) {
459                 createDom(v, el);
460             });
461         } else if (Ext.isString(o)) {         // Allow a string as a child spec.
462             el = doc.createTextNode(o);
463         } else {
464             el = doc.createElement( o.tag || 'div' );
465             useSet = !!el.setAttribute; // In IE some elements don't have setAttribute
466             Ext.iterate(o, function(attr, val){
467                 if(!/tag|children|cn|html|style/.test(attr)){
468                         if(attr == 'cls'){
469                             el.className = val;
470                         }else{
471                         if(useSet){
472                             el.setAttribute(attr, val);
473                         }else{
474                             el[attr] = val;
475                         }
476                         }
477                 }
478             });
479             Ext.DomHelper.applyStyles(el, o.style);
480
481             if ((cn = o.children || o.cn)) {
482                 createDom(cn, el);
483             } else if (o.html) {
484                 el.innerHTML = o.html;
485             }
486         }
487         if(parentNode){
488            parentNode.appendChild(el);
489         }
490         return el;
491     }
492
493         pub = {
494                 /**
495              * Creates a new Ext.Template from the DOM object spec.
496              * @param {Object} o The DOM object spec (and children)
497              * @return {Ext.Template} The new template
498              */
499             createTemplate : function(o){
500                 var html = Ext.DomHelper.createHtml(o);
501                 return new Ext.Template(html);
502             },
503
504                 /** True to force the use of DOM instead of html fragments @type Boolean */
505             useDom : false,
506
507             /**
508              * Creates new DOM element(s) and inserts them before el.
509              * @param {Mixed} el The context element
510              * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
511              * @param {Boolean} returnElement (optional) true to return a Ext.Element
512              * @return {HTMLElement/Ext.Element} The new node
513          * @hide (repeat)
514              */
515             insertBefore : function(el, o, returnElement){
516                 return doInsert(el, o, returnElement, beforebegin);
517             },
518
519             /**
520              * Creates new DOM element(s) and inserts them after el.
521              * @param {Mixed} el The context element
522              * @param {Object} o The DOM object spec (and children)
523              * @param {Boolean} returnElement (optional) true to return a Ext.Element
524              * @return {HTMLElement/Ext.Element} The new node
525          * @hide (repeat)
526              */
527             insertAfter : function(el, o, returnElement){
528                 return doInsert(el, o, returnElement, afterend, 'nextSibling');
529             },
530
531             /**
532              * Creates new DOM element(s) and inserts them as the first child of el.
533              * @param {Mixed} el The context element
534              * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
535              * @param {Boolean} returnElement (optional) true to return a Ext.Element
536              * @return {HTMLElement/Ext.Element} The new node
537          * @hide (repeat)
538              */
539             insertFirst : function(el, o, returnElement){
540                 return doInsert(el, o, returnElement, afterbegin, 'firstChild');
541             },
542
543             /**
544              * Creates new DOM element(s) and appends them to el.
545              * @param {Mixed} el The context element
546              * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
547              * @param {Boolean} returnElement (optional) true to return a Ext.Element
548              * @return {HTMLElement/Ext.Element} The new node
549          * @hide (repeat)
550              */
551             append: function(el, o, returnElement){
552             return doInsert(el, o, returnElement, beforeend, '', true);
553         },
554
555             /**
556              * Creates new DOM element(s) without inserting them to the document.
557              * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
558              * @return {HTMLElement} The new uninserted node
559              */
560         createDom: createDom
561         };
562         return pub;
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 };/**
801  * @class Ext.Template
802  */
803 Ext.apply(Ext.Template.prototype, {
804     /**
805      * @cfg {Boolean} disableFormats Specify <tt>true</tt> to disable format
806      * functions in the template. If the template does not contain
807      * {@link Ext.util.Format format functions}, setting <code>disableFormats</code>
808      * to true will reduce <code>{@link #apply}</code> time. Defaults to <tt>false</tt>.
809      * <pre><code>
810 var t = new Ext.Template(
811     '&lt;div name="{id}"&gt;',
812         '&lt;span class="{cls}"&gt;{name} {value}&lt;/span&gt;',
813     '&lt;/div&gt;',
814     {
815         compiled: true,      // {@link #compile} immediately
816         disableFormats: true // reduce <code>{@link #apply}</code> time since no formatting
817     }    
818 );
819      * </code></pre>
820      * For a list of available format functions, see {@link Ext.util.Format}.
821      */
822     disableFormats : false,                             
823     /**
824      * See <code>{@link #disableFormats}</code>.
825      * @type Boolean
826      * @property disableFormats
827      */
828
829     /**
830      * The regular expression used to match template variables
831      * @type RegExp
832      * @property
833      * @hide repeat doc
834      */
835     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
836
837     /**
838      * Returns an HTML fragment of this template with the specified values applied.
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'})
840      * @return {String} The HTML fragment
841      * @hide repeat doc
842      */
843     applyTemplate : function(values){
844                 var me = this,
845                         useF = me.disableFormats !== true,
846                 fm = Ext.util.Format, 
847                 tpl = me;           
848             
849         if(me.compiled){
850             return me.compiled(values);
851         }
852         function fn(m, name, format, args){
853             if (format && useF) {
854                 if (format.substr(0, 5) == "this.") {
855                     return tpl.call(format.substr(5), values[name], values);
856                 } else {
857                     if (args) {
858                         // quoted values are required for strings in compiled templates,
859                         // but for non compiled we need to strip them
860                         // quoted reversed for jsmin
861                         var re = /^\s*['"](.*)["']\s*$/;
862                         args = args.split(',');
863                         for(var i = 0, len = args.length; i < len; i++){
864                             args[i] = args[i].replace(re, "$1");
865                         }
866                         args = [values[name]].concat(args);
867                     } else {
868                         args = [values[name]];
869                     }
870                     return fm[format].apply(fm, args);
871                 }
872             } else {
873                 return values[name] !== undefined ? values[name] : "";
874             }
875         }
876         return me.html.replace(me.re, fn);
877     },
878                 
879     /**
880      * Compiles the template into an internal function, eliminating the RegEx overhead.
881      * @return {Ext.Template} this
882      * @hide repeat doc
883      */
884     compile : function(){
885         var me = this,
886                 fm = Ext.util.Format,
887                 useF = me.disableFormats !== true,
888                 sep = Ext.isGecko ? "+" : ",",
889                 body;
890         
891         function fn(m, name, format, args){
892             if(format && useF){
893                 args = args ? ',' + args : "";
894                 if(format.substr(0, 5) != "this."){
895                     format = "fm." + format + '(';
896                 }else{
897                     format = 'this.call("'+ format.substr(5) + '", ';
898                     args = ", values";
899                 }
900             }else{
901                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
902             }
903             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
904         }
905         
906         // branched to use + in gecko and [].join() in others
907         if(Ext.isGecko){
908             body = "this.compiled = function(values){ return '" +
909                    me.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
910                     "';};";
911         }else{
912             body = ["this.compiled = function(values){ return ['"];
913             body.push(me.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
914             body.push("'].join('');};");
915             body = body.join('');
916         }
917         eval(body);
918         return me;
919     },
920     
921     // private function used to call members
922     call : function(fnName, value, allValues){
923         return this[fnName](value, allValues);
924     }
925 });
926 Ext.Template.prototype.apply = Ext.Template.prototype.applyTemplate; /*
927  * This is code is also distributed under MIT license for use
928  * with jQuery and prototype JavaScript libraries.
929  */
930 /**
931  * @class Ext.DomQuery
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).
933 <p>
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>
935
936 <p>
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.
938 </p>
939 <h4>Element Selectors:</h4>
940 <ul class="list">
941     <li> <b>*</b> any element</li>
942     <li> <b>E</b> an element with the tag E</li>
943     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
944     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
945     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
946     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
947 </ul>
948 <h4>Attribute Selectors:</h4>
949 <p>The use of &#64; and quotes are optional. For example, div[&#64;foo='bar'] is also a valid attribute selector.</p>
950 <ul class="list">
951     <li> <b>E[foo]</b> has an attribute "foo"</li>
952     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
953     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
954     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
955     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
956     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
957     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
958 </ul>
959 <h4>Pseudo Classes:</h4>
960 <ul class="list">
961     <li> <b>E:first-child</b> E is the first child of its parent</li>
962     <li> <b>E:last-child</b> E is the last child of its parent</li>
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>
964     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
965     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
966     <li> <b>E:only-child</b> E is the only child of its parent</li>
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>
968     <li> <b>E:first</b> the first E in the resultset</li>
969     <li> <b>E:last</b> the last E in the resultset</li>
970     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
971     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
972     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
973     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
974     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
975     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
976     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
977     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
978     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
979     <li> <b>E:any(S1|S2|S2)</b> an E element which matches any of the simple selectors S1, S2 or S3//\\</li>
980 </ul>
981 <h4>CSS Value Selectors:</h4>
982 <ul class="list">
983     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
984     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
985     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
986     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
987     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
988     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
989 </ul>
990  * @singleton
991  */
992 Ext.DomQuery = function(){
993     var cache = {}, 
994         simpleCache = {}, 
995         valueCache = {},
996         nonSpace = /\S/,
997         trimRe = /^\s+|\s+$/g,
998         tplRe = /\{(\d+)\}/g,
999         modeRe = /^(\s?[\/>+~]\s?|\s|$)/,
1000         tagTokenRe = /^(#)?([\w-\*]+)/,
1001         nthRe = /(\d*)n\+?(\d*)/, 
1002         nthRe2 = /\D/,
1003         // This is for IE MSXML which does not support expandos.
1004         // IE runs the same speed using setAttribute, however FF slows way down
1005         // and Safari completely fails so they need to continue to use expandos.
1006         isIE = window.ActiveXObject ? true : false,
1007         key = 30803;
1008     
1009     // this eval is stop the compressor from
1010     // renaming the variable to something shorter
1011     eval("var batch = 30803;");         
1012
1013     // Retrieve the child node from a particular
1014     // parent at the specified index.
1015     function child(parent, index){
1016         var i = 0,
1017             n = parent.firstChild;
1018         while(n){
1019             if(n.nodeType == 1){
1020                if(++i == index){
1021                    return n;
1022                }
1023             }
1024             n = n.nextSibling;
1025         }
1026         return null;
1027     }
1028
1029     // retrieve the next element node
1030     function next(n){   
1031         while((n = n.nextSibling) && n.nodeType != 1);
1032         return n;
1033     }
1034
1035     // retrieve the previous element node 
1036     function prev(n){
1037         while((n = n.previousSibling) && n.nodeType != 1);
1038         return n;
1039     }
1040
1041     // Mark each child node with a nodeIndex skipping and
1042     // removing empty text nodes.
1043     function children(parent){
1044         var n = parent.firstChild,
1045             nodeIndex = -1,
1046             nextNode;
1047         while(n){
1048             nextNode = n.nextSibling;
1049             // clean worthless empty nodes.
1050             if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
1051                 parent.removeChild(n);
1052             }else{
1053                 // add an expando nodeIndex
1054                 n.nodeIndex = ++nodeIndex;
1055             }
1056             n = nextNode;
1057         }
1058         return this;
1059     }
1060
1061
1062     // nodeSet - array of nodes
1063     // cls - CSS Class
1064     function byClassName(nodeSet, cls){
1065         if(!cls){
1066             return nodeSet;
1067         }
1068         var result = [], ri = -1;
1069         for(var i = 0, ci; ci = nodeSet[i]; i++){
1070             if((' '+ci.className+' ').indexOf(cls) != -1){
1071                 result[++ri] = ci;
1072             }
1073         }
1074         return result;
1075     };
1076
1077     function attrValue(n, attr){
1078         // if its an array, use the first node.
1079         if(!n.tagName && typeof n.length != "undefined"){
1080             n = n[0];
1081         }
1082         if(!n){
1083             return null;
1084         }
1085
1086         if(attr == "for"){
1087             return n.htmlFor;
1088         }
1089         if(attr == "class" || attr == "className"){
1090             return n.className;
1091         }
1092         return n.getAttribute(attr) || n[attr];
1093
1094     };
1095
1096
1097     // ns - nodes
1098     // mode - false, /, >, +, ~
1099     // tagName - defaults to "*"
1100     function getNodes(ns, mode, tagName){
1101         var result = [], ri = -1, cs;
1102         if(!ns){
1103             return result;
1104         }
1105         tagName = tagName || "*";
1106         // convert to array
1107         if(typeof ns.getElementsByTagName != "undefined"){
1108             ns = [ns];
1109         }
1110         
1111         // no mode specified, grab all elements by tagName
1112         // at any depth
1113         if(!mode){
1114             for(var i = 0, ni; ni = ns[i]; i++){
1115                 cs = ni.getElementsByTagName(tagName);
1116                 for(var j = 0, ci; ci = cs[j]; j++){
1117                     result[++ri] = ci;
1118                 }
1119             }
1120         // Direct Child mode (/ or >)
1121         // E > F or E/F all direct children elements of E that have the tag     
1122         } else if(mode == "/" || mode == ">"){
1123             var utag = tagName.toUpperCase();
1124             for(var i = 0, ni, cn; ni = ns[i]; i++){
1125                 cn = ni.childNodes;
1126                 for(var j = 0, cj; cj = cn[j]; j++){
1127                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
1128                         result[++ri] = cj;
1129                     }
1130                 }
1131             }
1132         // Immediately Preceding mode (+)
1133         // E + F all elements with the tag F that are immediately preceded by an element with the tag E
1134         }else if(mode == "+"){
1135             var utag = tagName.toUpperCase();
1136             for(var i = 0, n; n = ns[i]; i++){
1137                 while((n = n.nextSibling) && n.nodeType != 1);
1138                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
1139                     result[++ri] = n;
1140                 }
1141             }
1142         // Sibling mode (~)
1143         // E ~ F all elements with the tag F that are preceded by a sibling element with the tag E
1144         }else if(mode == "~"){
1145             var utag = tagName.toUpperCase();
1146             for(var i = 0, n; n = ns[i]; i++){
1147                 while((n = n.nextSibling)){
1148                     if (n.nodeName == utag || n.nodeName == tagName || tagName == '*'){
1149                         result[++ri] = n;
1150                     }
1151                 }
1152             }
1153         }
1154         return result;
1155     }
1156
1157     function concat(a, b){
1158         if(b.slice){
1159             return a.concat(b);
1160         }
1161         for(var i = 0, l = b.length; i < l; i++){
1162             a[a.length] = b[i];
1163         }
1164         return a;
1165     }
1166
1167     function byTag(cs, tagName){
1168         if(cs.tagName || cs == document){
1169             cs = [cs];
1170         }
1171         if(!tagName){
1172             return cs;
1173         }
1174         var result = [], ri = -1;
1175         tagName = tagName.toLowerCase();
1176         for(var i = 0, ci; ci = cs[i]; i++){
1177             if(ci.nodeType == 1 && ci.tagName.toLowerCase() == tagName){
1178                 result[++ri] = ci;
1179             }
1180         }
1181         return result;
1182     }
1183
1184     function byId(cs, id){
1185         if(cs.tagName || cs == document){
1186             cs = [cs];
1187         }
1188         if(!id){
1189             return cs;
1190         }
1191         var result = [], ri = -1;
1192         for(var i = 0, ci; ci = cs[i]; i++){
1193             if(ci && ci.id == id){
1194                 result[++ri] = ci;
1195                 return result;
1196             }
1197         }
1198         return result;
1199     }
1200
1201     // operators are =, !=, ^=, $=, *=, %=, |= and ~=
1202     // custom can be "{"
1203     function byAttribute(cs, attr, value, op, custom){
1204         var result = [], 
1205             ri = -1, 
1206             useGetStyle = custom == "{",            
1207             fn = Ext.DomQuery.operators[op],        
1208             a,      
1209             innerHTML;
1210         for(var i = 0, ci; ci = cs[i]; i++){
1211             // skip non-element nodes.
1212             if(ci.nodeType != 1){
1213                 continue;
1214             }
1215             
1216             innerHTML = ci.innerHTML;
1217             // we only need to change the property names if we're dealing with html nodes, not XML
1218             if(innerHTML !== null && innerHTML !== undefined){
1219                 if(useGetStyle){
1220                     a = Ext.DomQuery.getStyle(ci, attr);
1221                 } else if (attr == "class" || attr == "className"){
1222                     a = ci.className;
1223                 } else if (attr == "for"){
1224                     a = ci.htmlFor;
1225                 } else if (attr == "href"){
1226                     // getAttribute href bug
1227                     // http://www.glennjones.net/Post/809/getAttributehrefbug.htm
1228                     a = ci.getAttribute("href", 2);
1229                 } else{
1230                     a = ci.getAttribute(attr);
1231                 }
1232             }else{
1233                 a = ci.getAttribute(attr);
1234             }
1235             if((fn && fn(a, value)) || (!fn && a)){
1236                 result[++ri] = ci;
1237             }
1238         }
1239         return result;
1240     }
1241
1242     function byPseudo(cs, name, value){
1243         return Ext.DomQuery.pseudos[name](cs, value);
1244     }
1245
1246     function nodupIEXml(cs){
1247         var d = ++key, 
1248             r;
1249         cs[0].setAttribute("_nodup", d);
1250         r = [cs[0]];
1251         for(var i = 1, len = cs.length; i < len; i++){
1252             var c = cs[i];
1253             if(!c.getAttribute("_nodup") != d){
1254                 c.setAttribute("_nodup", d);
1255                 r[r.length] = c;
1256             }
1257         }
1258         for(var i = 0, len = cs.length; i < len; i++){
1259             cs[i].removeAttribute("_nodup");
1260         }
1261         return r;
1262     }
1263
1264     function nodup(cs){
1265         if(!cs){
1266             return [];
1267         }
1268         var len = cs.length, c, i, r = cs, cj, ri = -1;
1269         if(!len || typeof cs.nodeType != "undefined" || len == 1){
1270             return cs;
1271         }
1272         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
1273             return nodupIEXml(cs);
1274         }
1275         var d = ++key;
1276         cs[0]._nodup = d;
1277         for(i = 1; c = cs[i]; i++){
1278             if(c._nodup != d){
1279                 c._nodup = d;
1280             }else{
1281                 r = [];
1282                 for(var j = 0; j < i; j++){
1283                     r[++ri] = cs[j];
1284                 }
1285                 for(j = i+1; cj = cs[j]; j++){
1286                     if(cj._nodup != d){
1287                         cj._nodup = d;
1288                         r[++ri] = cj;
1289                     }
1290                 }
1291                 return r;
1292             }
1293         }
1294         return r;
1295     }
1296
1297     function quickDiffIEXml(c1, c2){
1298         var d = ++key,
1299             r = [];
1300         for(var i = 0, len = c1.length; i < len; i++){
1301             c1[i].setAttribute("_qdiff", d);
1302         }        
1303         for(var i = 0, len = c2.length; i < len; i++){
1304             if(c2[i].getAttribute("_qdiff") != d){
1305                 r[r.length] = c2[i];
1306             }
1307         }
1308         for(var i = 0, len = c1.length; i < len; i++){
1309            c1[i].removeAttribute("_qdiff");
1310         }
1311         return r;
1312     }
1313
1314     function quickDiff(c1, c2){
1315         var len1 = c1.length,
1316                 d = ++key,
1317                 r = [];
1318         if(!len1){
1319             return c2;
1320         }
1321         if(isIE && typeof c1[0].selectSingleNode != "undefined"){
1322             return quickDiffIEXml(c1, c2);
1323         }        
1324         for(var i = 0; i < len1; i++){
1325             c1[i]._qdiff = d;
1326         }        
1327         for(var i = 0, len = c2.length; i < len; i++){
1328             if(c2[i]._qdiff != d){
1329                 r[r.length] = c2[i];
1330             }
1331         }
1332         return r;
1333     }
1334
1335     function quickId(ns, mode, root, id){
1336         if(ns == root){
1337            var d = root.ownerDocument || root;
1338            return d.getElementById(id);
1339         }
1340         ns = getNodes(ns, mode, "*");
1341         return byId(ns, id);
1342     }
1343
1344     return {
1345         getStyle : function(el, name){
1346             return Ext.fly(el).getStyle(name);
1347         },
1348         /**
1349          * Compiles a selector/xpath query into a reusable function. The returned function
1350          * takes one parameter "root" (optional), which is the context node from where the query should start.
1351          * @param {String} selector The selector/xpath query
1352          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
1353          * @return {Function}
1354          */
1355         compile : function(path, type){
1356             type = type || "select";
1357
1358             // setup fn preamble
1359             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"],
1360                 mode,           
1361                 lastPath,
1362                 matchers = Ext.DomQuery.matchers,
1363                 matchersLn = matchers.length,
1364                 modeMatch,
1365                 // accept leading mode switch
1366                 lmode = path.match(modeRe);
1367             
1368             if(lmode && lmode[1]){
1369                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
1370                 path = path.replace(lmode[1], "");
1371             }
1372             
1373             // strip leading slashes
1374             while(path.substr(0, 1)=="/"){
1375                 path = path.substr(1);
1376             }
1377
1378             while(path && lastPath != path){
1379                 lastPath = path;
1380                 var tokenMatch = path.match(tagTokenRe);
1381                 if(type == "select"){
1382                     if(tokenMatch){
1383                         // ID Selector
1384                         if(tokenMatch[1] == "#"){
1385                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tokenMatch[2]+'");';                 
1386                         }else{
1387                             fn[fn.length] = 'n = getNodes(n, mode, "'+tokenMatch[2]+'");';
1388                         }
1389                         path = path.replace(tokenMatch[0], "");
1390                     }else if(path.substr(0, 1) != '@'){
1391                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
1392                     }
1393                 // type of "simple"
1394                 }else{
1395                     if(tokenMatch){
1396                         if(tokenMatch[1] == "#"){
1397                             fn[fn.length] = 'n = byId(n, "'+tokenMatch[2]+'");';
1398                         }else{
1399                             fn[fn.length] = 'n = byTag(n, "'+tokenMatch[2]+'");';
1400                         }
1401                         path = path.replace(tokenMatch[0], "");
1402                     }
1403                 }
1404                 while(!(modeMatch = path.match(modeRe))){
1405                     var matched = false;
1406                     for(var j = 0; j < matchersLn; j++){
1407                         var t = matchers[j];
1408                         var m = path.match(t.re);
1409                         if(m){
1410                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
1411                                 return m[i];
1412                             });
1413                             path = path.replace(m[0], "");
1414                             matched = true;
1415                             break;
1416                         }
1417                     }
1418                     // prevent infinite loop on bad selector
1419                     if(!matched){
1420                         throw 'Error parsing selector, parsing failed at "' + path + '"';
1421                     }
1422                 }
1423                 if(modeMatch[1]){
1424                     fn[fn.length] = 'mode="'+modeMatch[1].replace(trimRe, "")+'";';
1425                     path = path.replace(modeMatch[1], "");
1426                 }
1427             }
1428             // close fn out
1429             fn[fn.length] = "return nodup(n);\n}";
1430             
1431             // eval fn and return it
1432             eval(fn.join(""));
1433             return f;
1434         },
1435
1436         /**
1437          * Selects a group of elements.
1438          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
1439          * @param {Node/String} root (optional) The start of the query (defaults to document).
1440          * @return {Array} An Array of DOM elements which match the selector. If there are
1441          * no matches, and empty Array is returned.
1442          */
1443         jsSelect: function(path, root, type){
1444             // set root to doc if not specified.
1445             root = root || document;
1446             
1447             if(typeof root == "string"){
1448                 root = document.getElementById(root);
1449             }
1450             var paths = path.split(","),
1451                 results = [];
1452                 
1453             // loop over each selector
1454             for(var i = 0, len = paths.length; i < len; i++){           
1455                 var subPath = paths[i].replace(trimRe, "");
1456                 // compile and place in cache
1457                 if(!cache[subPath]){
1458                     cache[subPath] = Ext.DomQuery.compile(subPath);
1459                     if(!cache[subPath]){
1460                         throw subPath + " is not a valid selector";
1461                     }
1462                 }
1463                 var result = cache[subPath](root);
1464                 if(result && result != document){
1465                     results = results.concat(result);
1466                 }
1467             }
1468             
1469             // if there were multiple selectors, make sure dups
1470             // are eliminated
1471             if(paths.length > 1){
1472                 return nodup(results);
1473             }
1474             return results;
1475         },
1476         isXml: function(el) {
1477             var docEl = (el ? el.ownerDocument || el : 0).documentElement;
1478             return docEl ? docEl.nodeName !== "HTML" : false;
1479         },
1480         select : document.querySelectorAll ? function(path, root, type) {
1481             root = root || document;
1482             if (!Ext.DomQuery.isXml(root)) {
1483                 try {
1484                     var cs = root.querySelectorAll(path);
1485                     return Ext.toArray(cs);
1486                 }
1487                 catch (ex) {}           
1488             }       
1489             return Ext.DomQuery.jsSelect.call(this, path, root, type);
1490         } : function(path, root, type) {
1491             return Ext.DomQuery.jsSelect.call(this, path, root, type);
1492         },
1493
1494         /**
1495          * Selects a single element.
1496          * @param {String} selector The selector/xpath query
1497          * @param {Node} root (optional) The start of the query (defaults to document).
1498          * @return {Element} The DOM element which matched the selector.
1499          */
1500         selectNode : function(path, root){
1501             return Ext.DomQuery.select(path, root)[0];
1502         },
1503
1504         /**
1505          * Selects the value of a node, optionally replacing null with the defaultValue.
1506          * @param {String} selector The selector/xpath query
1507          * @param {Node} root (optional) The start of the query (defaults to document).
1508          * @param {String} defaultValue
1509          * @return {String}
1510          */
1511         selectValue : function(path, root, defaultValue){
1512             path = path.replace(trimRe, "");
1513             if(!valueCache[path]){
1514                 valueCache[path] = Ext.DomQuery.compile(path, "select");
1515             }
1516             var n = valueCache[path](root), v;
1517             n = n[0] ? n[0] : n;
1518                     
1519             // overcome a limitation of maximum textnode size
1520             // Rumored to potentially crash IE6 but has not been confirmed.
1521             // http://reference.sitepoint.com/javascript/Node/normalize
1522             // https://developer.mozilla.org/En/DOM/Node.normalize          
1523             if (typeof n.normalize == 'function') n.normalize();
1524             
1525             v = (n && n.firstChild ? n.firstChild.nodeValue : null);
1526             return ((v === null||v === undefined||v==='') ? defaultValue : v);
1527         },
1528
1529         /**
1530          * Selects the value of a node, parsing integers and floats. Returns the defaultValue, or 0 if none is specified.
1531          * @param {String} selector The selector/xpath query
1532          * @param {Node} root (optional) The start of the query (defaults to document).
1533          * @param {Number} defaultValue
1534          * @return {Number}
1535          */
1536         selectNumber : function(path, root, defaultValue){
1537             var v = Ext.DomQuery.selectValue(path, root, defaultValue || 0);
1538             return parseFloat(v);
1539         },
1540
1541         /**
1542          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
1543          * @param {String/HTMLElement/Array} el An element id, element or array of elements
1544          * @param {String} selector The simple selector to test
1545          * @return {Boolean}
1546          */
1547         is : function(el, ss){
1548             if(typeof el == "string"){
1549                 el = document.getElementById(el);
1550             }
1551             var isArray = Ext.isArray(el),
1552                 result = Ext.DomQuery.filter(isArray ? el : [el], ss);
1553             return isArray ? (result.length == el.length) : (result.length > 0);
1554         },
1555
1556         /**
1557          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
1558          * @param {Array} el An array of elements to filter
1559          * @param {String} selector The simple selector to test
1560          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
1561          * the selector instead of the ones that match
1562          * @return {Array} An Array of DOM elements which match the selector. If there are
1563          * no matches, and empty Array is returned.
1564          */
1565         filter : function(els, ss, nonMatches){
1566             ss = ss.replace(trimRe, "");
1567             if(!simpleCache[ss]){
1568                 simpleCache[ss] = Ext.DomQuery.compile(ss, "simple");
1569             }
1570             var result = simpleCache[ss](els);
1571             return nonMatches ? quickDiff(result, els) : result;
1572         },
1573
1574         /**
1575          * Collection of matching regular expressions and code snippets.
1576          * Each capture group within () will be replace the {} in the select
1577          * statement as specified by their index.
1578          */
1579         matchers : [{
1580                 re: /^\.([\w-]+)/,
1581                 select: 'n = byClassName(n, " {1} ");'
1582             }, {
1583                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
1584                 select: 'n = byPseudo(n, "{1}", "{2}");'
1585             },{
1586                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
1587                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
1588             }, {
1589                 re: /^#([\w-]+)/,
1590                 select: 'n = byId(n, "{1}");'
1591             },{
1592                 re: /^@([\w-]+)/,
1593                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
1594             }
1595         ],
1596
1597         /**
1598          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
1599          * New operators can be added as long as the match the format <i>c</i>= where <i>c</i> is any character other than space, &gt; &lt;.
1600          */
1601         operators : {
1602             "=" : function(a, v){
1603                 return a == v;
1604             },
1605             "!=" : function(a, v){
1606                 return a != v;
1607             },
1608             "^=" : function(a, v){
1609                 return a && a.substr(0, v.length) == v;
1610             },
1611             "$=" : function(a, v){
1612                 return a && a.substr(a.length-v.length) == v;
1613             },
1614             "*=" : function(a, v){
1615                 return a && a.indexOf(v) !== -1;
1616             },
1617             "%=" : function(a, v){
1618                 return (a % v) == 0;
1619             },
1620             "|=" : function(a, v){
1621                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
1622             },
1623             "~=" : function(a, v){
1624                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
1625             }
1626         },
1627
1628         /**
1629          * <p>Object hash of "pseudo class" filter functions which are used when filtering selections. Each function is passed
1630          * two parameters:</p><div class="mdetail-params"><ul>
1631          * <li><b>c</b> : Array<div class="sub-desc">An Array of DOM elements to filter.</div></li>
1632          * <li><b>v</b> : String<div class="sub-desc">The argument (if any) supplied in the selector.</div></li>
1633          * </ul></div>
1634          * <p>A filter function returns an Array of DOM elements which conform to the pseudo class.</p>
1635          * <p>In addition to the provided pseudo classes listed above such as <code>first-child</code> and <code>nth-child</code>,
1636          * developers may add additional, custom psuedo class filters to select elements according to application-specific requirements.</p>
1637          * <p>For example, to filter <code>&lt;a></code> elements to only return links to <i>external</i> resources:</p>
1638          * <code><pre>
1639 Ext.DomQuery.pseudos.external = function(c, v){
1640     var r = [], ri = -1;
1641     for(var i = 0, ci; ci = c[i]; i++){
1642 //      Include in result set only if it's a link to an external resource
1643         if(ci.hostname != location.hostname){
1644             r[++ri] = ci;
1645         }
1646     }
1647     return r;
1648 };</pre></code>
1649          * Then external links could be gathered with the following statement:<code><pre>
1650 var externalLinks = Ext.select("a:external");
1651 </code></pre>
1652          */
1653         pseudos : {
1654             "first-child" : function(c){
1655                 var r = [], ri = -1, n;
1656                 for(var i = 0, ci; ci = n = c[i]; i++){
1657                     while((n = n.previousSibling) && n.nodeType != 1);
1658                     if(!n){
1659                         r[++ri] = ci;
1660                     }
1661                 }
1662                 return r;
1663             },
1664
1665             "last-child" : function(c){
1666                 var r = [], ri = -1, n;
1667                 for(var i = 0, ci; ci = n = c[i]; i++){
1668                     while((n = n.nextSibling) && n.nodeType != 1);
1669                     if(!n){
1670                         r[++ri] = ci;
1671                     }
1672                 }
1673                 return r;
1674             },
1675
1676             "nth-child" : function(c, a) {
1677                 var r = [], ri = -1,
1678                         m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a),
1679                         f = (m[1] || 1) - 0, l = m[2] - 0;
1680                 for(var i = 0, n; n = c[i]; i++){
1681                     var pn = n.parentNode;
1682                     if (batch != pn._batch) {
1683                         var j = 0;
1684                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
1685                             if(cn.nodeType == 1){
1686                                cn.nodeIndex = ++j;
1687                             }
1688                         }
1689                         pn._batch = batch;
1690                     }
1691                     if (f == 1) {
1692                         if (l == 0 || n.nodeIndex == l){
1693                             r[++ri] = n;
1694                         }
1695                     } else if ((n.nodeIndex + l) % f == 0){
1696                         r[++ri] = n;
1697                     }
1698                 }
1699
1700                 return r;
1701             },
1702
1703             "only-child" : function(c){
1704                 var r = [], ri = -1;;
1705                 for(var i = 0, ci; ci = c[i]; i++){
1706                     if(!prev(ci) && !next(ci)){
1707                         r[++ri] = ci;
1708                     }
1709                 }
1710                 return r;
1711             },
1712
1713             "empty" : function(c){
1714                 var r = [], ri = -1;
1715                 for(var i = 0, ci; ci = c[i]; i++){
1716                     var cns = ci.childNodes, j = 0, cn, empty = true;
1717                     while(cn = cns[j]){
1718                         ++j;
1719                         if(cn.nodeType == 1 || cn.nodeType == 3){
1720                             empty = false;
1721                             break;
1722                         }
1723                     }
1724                     if(empty){
1725                         r[++ri] = ci;
1726                     }
1727                 }
1728                 return r;
1729             },
1730
1731             "contains" : function(c, v){
1732                 var r = [], ri = -1;
1733                 for(var i = 0, ci; ci = c[i]; i++){
1734                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
1735                         r[++ri] = ci;
1736                     }
1737                 }
1738                 return r;
1739             },
1740
1741             "nodeValue" : function(c, v){
1742                 var r = [], ri = -1;
1743                 for(var i = 0, ci; ci = c[i]; i++){
1744                     if(ci.firstChild && ci.firstChild.nodeValue == v){
1745                         r[++ri] = ci;
1746                     }
1747                 }
1748                 return r;
1749             },
1750
1751             "checked" : function(c){
1752                 var r = [], ri = -1;
1753                 for(var i = 0, ci; ci = c[i]; i++){
1754                     if(ci.checked == true){
1755                         r[++ri] = ci;
1756                     }
1757                 }
1758                 return r;
1759             },
1760
1761             "not" : function(c, ss){
1762                 return Ext.DomQuery.filter(c, ss, true);
1763             },
1764
1765             "any" : function(c, selectors){
1766                 var ss = selectors.split('|'),
1767                         r = [], ri = -1, s;
1768                 for(var i = 0, ci; ci = c[i]; i++){
1769                     for(var j = 0; s = ss[j]; j++){
1770                         if(Ext.DomQuery.is(ci, s)){
1771                             r[++ri] = ci;
1772                             break;
1773                         }
1774                     }
1775                 }
1776                 return r;
1777             },
1778
1779             "odd" : function(c){
1780                 return this["nth-child"](c, "odd");
1781             },
1782
1783             "even" : function(c){
1784                 return this["nth-child"](c, "even");
1785             },
1786
1787             "nth" : function(c, a){
1788                 return c[a-1] || [];
1789             },
1790
1791             "first" : function(c){
1792                 return c[0] || [];
1793             },
1794
1795             "last" : function(c){
1796                 return c[c.length-1] || [];
1797             },
1798
1799             "has" : function(c, ss){
1800                 var s = Ext.DomQuery.select,
1801                         r = [], ri = -1;
1802                 for(var i = 0, ci; ci = c[i]; i++){
1803                     if(s(ss, ci).length > 0){
1804                         r[++ri] = ci;
1805                     }
1806                 }
1807                 return r;
1808             },
1809
1810             "next" : function(c, ss){
1811                 var is = Ext.DomQuery.is,
1812                         r = [], ri = -1;
1813                 for(var i = 0, ci; ci = c[i]; i++){
1814                     var n = next(ci);
1815                     if(n && is(n, ss)){
1816                         r[++ri] = ci;
1817                     }
1818                 }
1819                 return r;
1820             },
1821
1822             "prev" : function(c, ss){
1823                 var is = Ext.DomQuery.is,
1824                         r = [], ri = -1;
1825                 for(var i = 0, ci; ci = c[i]; i++){
1826                     var n = prev(ci);
1827                     if(n && is(n, ss)){
1828                         r[++ri] = ci;
1829                     }
1830                 }
1831                 return r;
1832             }
1833         }
1834     };
1835 }();
1836
1837 /**
1838  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Ext.DomQuery#select}
1839  * @param {String} path The selector/xpath query
1840  * @param {Node} root (optional) The start of the query (defaults to document).
1841  * @return {Array}
1842  * @member Ext
1843  * @method query
1844  */
1845 Ext.query = Ext.DomQuery.select;
1846 /**
1847  * @class Ext.util.DelayedTask
1848  * <p> The DelayedTask class provides a convenient way to "buffer" the execution of a method,
1849  * performing setTimeout where a new timeout cancels the old timeout. When called, the
1850  * task will wait the specified time period before executing. If durng that time period,
1851  * the task is called again, the original call will be cancelled. This continues so that
1852  * the function is only called a single time for each iteration.</p>
1853  * <p>This method is especially useful for things like detecting whether a user has finished
1854  * typing in a text field. An example would be performing validation on a keypress. You can
1855  * use this class to buffer the keypress events for a certain number of milliseconds, and
1856  * perform only if they stop for that amount of time.  Usage:</p><pre><code>
1857 var task = new Ext.util.DelayedTask(function(){
1858     alert(Ext.getDom('myInputField').value.length);
1859 });
1860 // Wait 500ms before calling our function. If the user presses another key 
1861 // during that 500ms, it will be cancelled and we'll wait another 500ms.
1862 Ext.get('myInputField').on('keypress', function(){
1863     task.{@link #delay}(500); 
1864 });
1865  * </code></pre> 
1866  * <p>Note that we are using a DelayedTask here to illustrate a point. The configuration
1867  * option <tt>buffer</tt> for {@link Ext.util.Observable#addListener addListener/on} will
1868  * also setup a delayed task for you to buffer events.</p> 
1869  * @constructor The parameters to this constructor serve as defaults and are not required.
1870  * @param {Function} fn (optional) The default function to call.
1871  * @param {Object} scope The default scope (The <code><b>this</b></code> reference) in which the
1872  * function is called. If not specified, <code>this</code> will refer to the browser window.
1873  * @param {Array} args (optional) The default Array of arguments.
1874  */
1875 Ext.util.DelayedTask = function(fn, scope, args){
1876     var me = this,
1877         id,     
1878         call = function(){
1879                 clearInterval(id);
1880                 id = null;
1881                 fn.apply(scope, args || []);
1882             };
1883             
1884     /**
1885      * Cancels any pending timeout and queues a new one
1886      * @param {Number} delay The milliseconds to delay
1887      * @param {Function} newFn (optional) Overrides function passed to constructor
1888      * @param {Object} newScope (optional) Overrides scope passed to constructor. Remember that if no scope
1889      * is specified, <code>this</code> will refer to the browser window.
1890      * @param {Array} newArgs (optional) Overrides args passed to constructor
1891      */
1892     me.delay = function(delay, newFn, newScope, newArgs){
1893         me.cancel();
1894         fn = newFn || fn;
1895         scope = newScope || scope;
1896         args = newArgs || args;
1897         id = setInterval(call, delay);
1898     };
1899
1900     /**
1901      * Cancel the last queued timeout
1902      */
1903     me.cancel = function(){
1904         if(id){
1905             clearInterval(id);
1906             id = null;
1907         }
1908     };
1909 };(function(){
1910
1911 var EXTUTIL = Ext.util,
1912     TOARRAY = Ext.toArray,
1913     EACH = Ext.each,
1914     ISOBJECT = Ext.isObject,
1915     TRUE = true,
1916     FALSE = false;
1917 /**
1918  * @class Ext.util.Observable
1919  * Base class that provides a common interface for publishing events. Subclasses are expected to
1920  * to have a property "events" with all the events defined, and, optionally, a property "listeners"
1921  * with configured listeners defined.<br>
1922  * For example:
1923  * <pre><code>
1924 Employee = Ext.extend(Ext.util.Observable, {
1925     constructor: function(config){
1926         this.name = config.name;
1927         this.addEvents({
1928             "fired" : true,
1929             "quit" : true
1930         });
1931
1932         // Copy configured listeners into *this* object so that the base class&#39;s
1933         // constructor will add them.
1934         this.listeners = config.listeners;
1935
1936         // Call our superclass constructor to complete construction process.
1937         Employee.superclass.constructor.call(this, config)
1938     }
1939 });
1940 </code></pre>
1941  * This could then be used like this:<pre><code>
1942 var newEmployee = new Employee({
1943     name: employeeName,
1944     listeners: {
1945         quit: function() {
1946             // By default, "this" will be the object that fired the event.
1947             alert(this.name + " has quit!");
1948         }
1949     }
1950 });
1951 </code></pre>
1952  */
1953 EXTUTIL.Observable = function(){
1954     /**
1955      * @cfg {Object} listeners (optional) <p>A config object containing one or more event handlers to be added to this
1956      * object during initialization.  This should be a valid listeners config object as specified in the
1957      * {@link #addListener} example for attaching multiple handlers at once.</p>
1958      * <br><p><b><u>DOM events from ExtJs {@link Ext.Component Components}</u></b></p>
1959      * <br><p>While <i>some</i> ExtJs Component classes export selected DOM events (e.g. "click", "mouseover" etc), this
1960      * is usually only done when extra value can be added. For example the {@link Ext.DataView DataView}'s
1961      * <b><code>{@link Ext.DataView#click click}</code></b> event passing the node clicked on. To access DOM
1962      * events directly from a Component's HTMLElement, listeners must be added to the <i>{@link Ext.Component#getEl Element}</i> after the Component
1963      * has been rendered. A plugin can simplify this step:<pre><code>
1964 // Plugin is configured with a listeners config object.
1965 // The Component is appended to the argument list of all handler functions.
1966 Ext.DomObserver = Ext.extend(Object, {
1967     constructor: function(config) {
1968         this.listeners = config.listeners ? config.listeners : config;
1969     },
1970
1971     // Component passes itself into plugin&#39;s init method
1972     init: function(c) {
1973         var p, l = this.listeners;
1974         for (p in l) {
1975             if (Ext.isFunction(l[p])) {
1976                 l[p] = this.createHandler(l[p], c);
1977             } else {
1978                 l[p].fn = this.createHandler(l[p].fn, c);
1979             }
1980         }
1981
1982         // Add the listeners to the Element immediately following the render call
1983         c.render = c.render.{@link Function#createSequence createSequence}(function() {
1984             var e = c.getEl();
1985             if (e) {
1986                 e.on(l);
1987             }
1988         });
1989     },
1990
1991     createHandler: function(fn, c) {
1992         return function(e) {
1993             fn.call(this, e, c);
1994         };
1995     }
1996 });
1997
1998 var combo = new Ext.form.ComboBox({
1999
2000     // Collapse combo when its element is clicked on
2001     plugins: [ new Ext.DomObserver({
2002         click: function(evt, comp) {
2003             comp.collapse();
2004         }
2005     })],
2006     store: myStore,
2007     typeAhead: true,
2008     mode: 'local',
2009     triggerAction: 'all'
2010 });
2011      * </code></pre></p>
2012      */
2013     var me = this, e = me.events;
2014     if(me.listeners){
2015         me.on(me.listeners);
2016         delete me.listeners;
2017     }
2018     me.events = e || {};
2019 };
2020
2021 EXTUTIL.Observable.prototype = {
2022     // private
2023     filterOptRe : /^(?:scope|delay|buffer|single)$/,
2024
2025     /**
2026      * <p>Fires the specified event with the passed parameters (minus the event name).</p>
2027      * <p>An event may be set to bubble up an Observable parent hierarchy (See {@link Ext.Component#getBubbleTarget})
2028      * by calling {@link #enableBubble}.</p>
2029      * @param {String} eventName The name of the event to fire.
2030      * @param {Object...} args Variable number of parameters are passed to handlers.
2031      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true.
2032      */
2033     fireEvent : function(){
2034         var a = TOARRAY(arguments),
2035             ename = a[0].toLowerCase(),
2036             me = this,
2037             ret = TRUE,
2038             ce = me.events[ename],
2039             q,
2040             c;
2041         if (me.eventsSuspended === TRUE) {
2042             if (q = me.eventQueue) {
2043                 q.push(a);
2044             }
2045         }
2046         else if(ISOBJECT(ce) && ce.bubble){
2047             if(ce.fire.apply(ce, a.slice(1)) === FALSE) {
2048                 return FALSE;
2049             }
2050             c = me.getBubbleTarget && me.getBubbleTarget();
2051             if(c && c.enableBubble) {
2052                 if(!c.events[ename] || !Ext.isObject(c.events[ename]) || !c.events[ename].bubble) {
2053                     c.enableBubble(ename);
2054                 }
2055                 return c.fireEvent.apply(c, a);
2056             }
2057         }
2058         else {
2059             if (ISOBJECT(ce)) {
2060                 a.shift();
2061                 ret = ce.fire.apply(ce, a);
2062             }
2063         }
2064         return ret;
2065     },
2066
2067     /**
2068      * Appends an event handler to this object.
2069      * @param {String}   eventName The name of the event to listen for.
2070      * @param {Function} handler The method the event invokes.
2071      * @param {Object}   scope (optional) The scope (<code><b>this</b></code> reference) in which the handler function is executed.
2072      * <b>If omitted, defaults to the object which fired the event.</b>
2073      * @param {Object}   options (optional) An object containing handler configuration.
2074      * properties. This may contain any of the following properties:<ul>
2075      * <li><b>scope</b> : Object<div class="sub-desc">The scope (<code><b>this</b></code> reference) in which the handler function is executed.
2076      * <b>If omitted, defaults to the object which fired the event.</b></div></li>
2077      * <li><b>delay</b> : Number<div class="sub-desc">The number of milliseconds to delay the invocation of the handler after the event fires.</div></li>
2078      * <li><b>single</b> : Boolean<div class="sub-desc">True to add a handler to handle just the next firing of the event, and then remove itself.</div></li>
2079      * <li><b>buffer</b> : Number<div class="sub-desc">Causes the handler to be scheduled to run in an {@link Ext.util.DelayedTask} delayed
2080      * by the specified number of milliseconds. If the event fires again within that time, the original
2081      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</div></li>
2082      * <li><b>target</b> : Observable<div class="sub-desc">Only call the handler if the event was fired on the target Observable, <i>not</i>
2083      * if the event was bubbled up from a child Observable.</div></li>
2084      * </ul><br>
2085      * <p>
2086      * <b>Combining Options</b><br>
2087      * Using the options argument, it is possible to combine different types of listeners:<br>
2088      * <br>
2089      * A delayed, one-time listener.
2090      * <pre><code>
2091 myDataView.on('click', this.onClick, this, {
2092 single: true,
2093 delay: 100
2094 });</code></pre>
2095      * <p>
2096      * <b>Attaching multiple handlers in 1 call</b><br>
2097      * The method also allows for a single argument to be passed which is a config object containing properties
2098      * which specify multiple handlers.
2099      * <p>
2100      * <pre><code>
2101 myGridPanel.on({
2102 'click' : {
2103     fn: this.onClick,
2104     scope: this,
2105     delay: 100
2106 },
2107 'mouseover' : {
2108     fn: this.onMouseOver,
2109     scope: this
2110 },
2111 'mouseout' : {
2112     fn: this.onMouseOut,
2113     scope: this
2114 }
2115 });</code></pre>
2116  * <p>
2117  * Or a shorthand syntax:<br>
2118  * <pre><code>
2119 myGridPanel.on({
2120 'click' : this.onClick,
2121 'mouseover' : this.onMouseOver,
2122 'mouseout' : this.onMouseOut,
2123  scope: this
2124 });</code></pre>
2125      */
2126     addListener : function(eventName, fn, scope, o){
2127         var me = this,
2128             e,
2129             oe,
2130             isF,
2131         ce;
2132         if (ISOBJECT(eventName)) {
2133             o = eventName;
2134             for (e in o){
2135                 oe = o[e];
2136                 if (!me.filterOptRe.test(e)) {
2137                     me.addListener(e, oe.fn || oe, oe.scope || o.scope, oe.fn ? oe : o);
2138                 }
2139             }
2140         } else {
2141             eventName = eventName.toLowerCase();
2142             ce = me.events[eventName] || TRUE;
2143             if (Ext.isBoolean(ce)) {
2144                 me.events[eventName] = ce = new EXTUTIL.Event(me, eventName);
2145             }
2146             ce.addListener(fn, scope, ISOBJECT(o) ? o : {});
2147         }
2148     },
2149
2150     /**
2151      * Removes an event handler.
2152      * @param {String}   eventName The type of event the handler was associated with.
2153      * @param {Function} handler   The handler to remove. <b>This must be a reference to the function passed into the {@link #addListener} call.</b>
2154      * @param {Object}   scope     (optional) The scope originally specified for the handler.
2155      */
2156     removeListener : function(eventName, fn, scope){
2157         var ce = this.events[eventName.toLowerCase()];
2158         if (ISOBJECT(ce)) {
2159             ce.removeListener(fn, scope);
2160         }
2161     },
2162
2163     /**
2164      * Removes all listeners for this object
2165      */
2166     purgeListeners : function(){
2167         var events = this.events,
2168             evt,
2169             key;
2170         for(key in events){
2171             evt = events[key];
2172             if(ISOBJECT(evt)){
2173                 evt.clearListeners();
2174             }
2175         }
2176     },
2177
2178     /**
2179      * Adds the specified events to the list of events which this Observable may fire.
2180      * @param {Object|String} o Either an object with event names as properties with a value of <code>true</code>
2181      * or the first event name string if multiple event names are being passed as separate parameters.
2182      * @param {string} Optional. Event name if multiple event names are being passed as separate parameters.
2183      * Usage:<pre><code>
2184 this.addEvents('storeloaded', 'storecleared');
2185 </code></pre>
2186      */
2187     addEvents : function(o){
2188         var me = this;
2189         me.events = me.events || {};
2190         if (Ext.isString(o)) {
2191             var a = arguments,
2192                 i = a.length;
2193             while(i--) {
2194                 me.events[a[i]] = me.events[a[i]] || TRUE;
2195             }
2196         } else {
2197             Ext.applyIf(me.events, o);
2198         }
2199     },
2200
2201     /**
2202      * Checks to see if this object has any listeners for a specified event
2203      * @param {String} eventName The name of the event to check for
2204      * @return {Boolean} True if the event is being listened for, else false
2205      */
2206     hasListener : function(eventName){
2207         var e = this.events[eventName.toLowerCase()];
2208         return ISOBJECT(e) && e.listeners.length > 0;
2209     },
2210
2211     /**
2212      * Suspend the firing of all events. (see {@link #resumeEvents})
2213      * @param {Boolean} queueSuspended Pass as true to queue up suspended events to be fired
2214      * after the {@link #resumeEvents} call instead of discarding all suspended events;
2215      */
2216     suspendEvents : function(queueSuspended){
2217         this.eventsSuspended = TRUE;
2218         if(queueSuspended && !this.eventQueue){
2219             this.eventQueue = [];
2220         }
2221     },
2222
2223     /**
2224      * Resume firing events. (see {@link #suspendEvents})
2225      * If events were suspended using the <tt><b>queueSuspended</b></tt> parameter, then all
2226      * events fired during event suspension will be sent to any listeners now.
2227      */
2228     resumeEvents : function(){
2229         var me = this,
2230             queued = me.eventQueue || [];
2231         me.eventsSuspended = FALSE;
2232         delete me.eventQueue;
2233         EACH(queued, function(e) {
2234             me.fireEvent.apply(me, e);
2235         });
2236     }
2237 };
2238
2239 var OBSERVABLE = EXTUTIL.Observable.prototype;
2240 /**
2241  * Appends an event handler to this object (shorthand for {@link #addListener}.)
2242  * @param {String}   eventName     The type of event to listen for
2243  * @param {Function} handler       The method the event invokes
2244  * @param {Object}   scope         (optional) The scope (<code><b>this</b></code> reference) in which the handler function is executed.
2245  * <b>If omitted, defaults to the object which fired the event.</b>
2246  * @param {Object}   options       (optional) An object containing handler configuration.
2247  * @method
2248  */
2249 OBSERVABLE.on = OBSERVABLE.addListener;
2250 /**
2251  * Removes an event handler (shorthand for {@link #removeListener}.)
2252  * @param {String}   eventName     The type of event the handler was associated with.
2253  * @param {Function} handler       The handler to remove. <b>This must be a reference to the function passed into the {@link #addListener} call.</b>
2254  * @param {Object}   scope         (optional) The scope originally specified for the handler.
2255  * @method
2256  */
2257 OBSERVABLE.un = OBSERVABLE.removeListener;
2258
2259 /**
2260  * Removes <b>all</b> added captures from the Observable.
2261  * @param {Observable} o The Observable to release
2262  * @static
2263  */
2264 EXTUTIL.Observable.releaseCapture = function(o){
2265     o.fireEvent = OBSERVABLE.fireEvent;
2266 };
2267
2268 function createTargeted(h, o, scope){
2269     return function(){
2270         if(o.target == arguments[0]){
2271             h.apply(scope, TOARRAY(arguments));
2272         }
2273     };
2274 };
2275
2276 function createBuffered(h, o, l, scope){
2277     l.task = new EXTUTIL.DelayedTask();
2278     return function(){
2279         l.task.delay(o.buffer, h, scope, TOARRAY(arguments));
2280     };
2281 };
2282
2283 function createSingle(h, e, fn, scope){
2284     return function(){
2285         e.removeListener(fn, scope);
2286         return h.apply(scope, arguments);
2287     };
2288 };
2289
2290 function createDelayed(h, o, l, scope){
2291     return function(){
2292         var task = new EXTUTIL.DelayedTask();
2293         if(!l.tasks) {
2294             l.tasks = [];
2295         }
2296         l.tasks.push(task);
2297         task.delay(o.delay || 10, h, scope, TOARRAY(arguments));
2298     };
2299 };
2300
2301 EXTUTIL.Event = function(obj, name){
2302     this.name = name;
2303     this.obj = obj;
2304     this.listeners = [];
2305 };
2306
2307 EXTUTIL.Event.prototype = {
2308     addListener : function(fn, scope, options){
2309         var me = this,
2310             l;
2311         scope = scope || me.obj;
2312         if(!me.isListening(fn, scope)){
2313             l = me.createListener(fn, scope, options);
2314             if(me.firing){ // if we are currently firing this event, don't disturb the listener loop
2315                 me.listeners = me.listeners.slice(0);
2316             }
2317             me.listeners.push(l);
2318         }
2319     },
2320
2321     createListener: function(fn, scope, o){
2322         o = o || {}, scope = scope || this.obj;
2323         var l = {
2324             fn: fn,
2325             scope: scope,
2326             options: o
2327         }, h = fn;
2328         if(o.target){
2329             h = createTargeted(h, o, scope);
2330         }
2331         if(o.delay){
2332             h = createDelayed(h, o, l, scope);
2333         }
2334         if(o.single){
2335             h = createSingle(h, this, fn, scope);
2336         }
2337         if(o.buffer){
2338             h = createBuffered(h, o, l, scope);
2339         }
2340         l.fireFn = h;
2341         return l;
2342     },
2343
2344     findListener : function(fn, scope){
2345         var list = this.listeners,
2346             i = list.length,
2347             l;
2348             
2349         scope = scope || this.obj;
2350         while(i--){
2351             l = list[i];
2352             if(l){
2353                 if(l.fn == fn && l.scope == scope){
2354                     return i;
2355                 }
2356             }
2357         }
2358         return -1;
2359     },
2360
2361     isListening : function(fn, scope){
2362         return this.findListener(fn, scope) != -1;
2363     },
2364
2365     removeListener : function(fn, scope){
2366         var index,
2367             l,
2368             k,
2369             me = this,
2370             ret = FALSE;
2371         if((index = me.findListener(fn, scope)) != -1){
2372             if (me.firing) {
2373                 me.listeners = me.listeners.slice(0);
2374             }
2375             l = me.listeners[index];
2376             if(l.task) {
2377                 l.task.cancel();
2378                 delete l.task;
2379             }
2380             k = l.tasks && l.tasks.length;
2381             if(k) {
2382                 while(k--) {
2383                     l.tasks[k].cancel();
2384                 }
2385                 delete l.tasks;
2386             }
2387             me.listeners.splice(index, 1);
2388             ret = TRUE;
2389         }
2390         return ret;
2391     },
2392
2393     // Iterate to stop any buffered/delayed events
2394     clearListeners : function(){
2395         var me = this,
2396             l = me.listeners,
2397             i = l.length;
2398         while(i--) {
2399             me.removeListener(l[i].fn, l[i].scope);
2400         }
2401     },
2402
2403     fire : function(){
2404         var me = this,
2405             args = TOARRAY(arguments),
2406             listeners = me.listeners,
2407             len = listeners.length,
2408             i = 0,
2409             l;
2410
2411         if(len > 0){
2412             me.firing = TRUE;
2413             for (; i < len; i++) {
2414                 l = listeners[i];
2415                 if(l && l.fireFn.apply(l.scope || me.obj || window, args) === FALSE) {
2416                     return (me.firing = FALSE);
2417                 }
2418             }
2419         }
2420         me.firing = FALSE;
2421         return TRUE;
2422     }
2423 };
2424 })();/**
2425  * @class Ext.util.Observable
2426  */
2427 Ext.apply(Ext.util.Observable.prototype, function(){
2428     // this is considered experimental (along with beforeMethod, afterMethod, removeMethodListener?)
2429     // allows for easier interceptor and sequences, including cancelling and overwriting the return value of the call
2430     // private
2431     function getMethodEvent(method){
2432         var e = (this.methodEvents = this.methodEvents ||
2433         {})[method], returnValue, v, cancel, obj = this;
2434
2435         if (!e) {
2436             this.methodEvents[method] = e = {};
2437             e.originalFn = this[method];
2438             e.methodName = method;
2439             e.before = [];
2440             e.after = [];
2441
2442             var makeCall = function(fn, scope, args){
2443                 if (!Ext.isEmpty(v = fn.apply(scope || obj, args))) {
2444                     if (Ext.isObject(v)) {
2445                         returnValue = !Ext.isEmpty(v.returnValue) ? v.returnValue : v;
2446                         cancel = !!v.cancel;
2447                     }
2448                     else
2449                         if (v === false) {
2450                             cancel = true;
2451                         }
2452                         else {
2453                             returnValue = v;
2454                         }
2455                 }
2456             };
2457
2458             this[method] = function(){
2459                 var args = Ext.toArray(arguments);
2460                 returnValue = v = undefined;
2461                 cancel = false;
2462
2463                 Ext.each(e.before, function(b){
2464                     makeCall(b.fn, b.scope, args);
2465                     if (cancel) {
2466                         return returnValue;
2467                     }
2468                 });
2469
2470                 if (!Ext.isEmpty(v = e.originalFn.apply(obj, args))) {
2471                     returnValue = v;
2472                 }
2473                 Ext.each(e.after, function(a){
2474                     makeCall(a.fn, a.scope, args);
2475                     if (cancel) {
2476                         return returnValue;
2477                     }
2478                 });
2479                 return returnValue;
2480             };
2481         }
2482         return e;
2483     }
2484
2485     return {
2486         // these are considered experimental
2487         // allows for easier interceptor and sequences, including cancelling and overwriting the return value of the call
2488         // adds an 'interceptor' called before the original method
2489         beforeMethod : function(method, fn, scope){
2490             getMethodEvent.call(this, method).before.push({
2491                 fn: fn,
2492                 scope: scope
2493             });
2494         },
2495
2496         // adds a 'sequence' called after the original method
2497         afterMethod : function(method, fn, scope){
2498             getMethodEvent.call(this, method).after.push({
2499                 fn: fn,
2500                 scope: scope
2501             });
2502         },
2503
2504         removeMethodListener: function(method, fn, scope){
2505             var e = getMethodEvent.call(this, method), found = false;
2506             Ext.each(e.before, function(b, i, arr){
2507                 if (b.fn == fn && b.scope == scope) {
2508                     arr.splice(i, 1);
2509                     found = true;
2510                     return false;
2511                 }
2512             });
2513             if (!found) {
2514                 Ext.each(e.after, function(a, i, arr){
2515                     if (a.fn == fn && a.scope == scope) {
2516                         arr.splice(i, 1);
2517                         return false;
2518                     }
2519                 });
2520             }
2521         },
2522
2523         /**
2524          * Relays selected events from the specified Observable as if the events were fired by <tt><b>this</b></tt>.
2525          * @param {Object} o The Observable whose events this object is to relay.
2526          * @param {Array} events Array of event names to relay.
2527          */
2528         relayEvents : function(o, events){
2529             var me = this;
2530             function createHandler(ename){
2531                 return function(){
2532                     return me.fireEvent.apply(me, [ename].concat(Ext.toArray(arguments)));
2533                 };
2534             }
2535             Ext.each(events, function(ename){
2536                 me.events[ename] = me.events[ename] || true;
2537                 o.on(ename, createHandler(ename), me);
2538             });
2539         },
2540
2541         /**
2542          * <p>Enables events fired by this Observable to bubble up an owner hierarchy by calling
2543          * <code>this.getBubbleTarget()</code> if present. There is no implementation in the Observable base class.</p>
2544          * <p>This is commonly used by Ext.Components to bubble events to owner Containers. See {@link Ext.Component.getBubbleTarget}. The default
2545          * implementation in Ext.Component returns the Component's immediate owner. But if a known target is required, this can be overridden to
2546          * access the required target more quickly.</p>
2547          * <p>Example:</p><pre><code>
2548 Ext.override(Ext.form.Field, {
2549     //  Add functionality to Field&#39;s initComponent to enable the change event to bubble
2550     initComponent : Ext.form.Field.prototype.initComponent.createSequence(function() {
2551         this.enableBubble('change');
2552     }),
2553
2554     //  We know that we want Field&#39;s events to bubble directly to the FormPanel.
2555     getBubbleTarget : function() {
2556         if (!this.formPanel) {
2557             this.formPanel = this.findParentByType('form');
2558         }
2559         return this.formPanel;
2560     }
2561 });
2562
2563 var myForm = new Ext.formPanel({
2564     title: 'User Details',
2565     items: [{
2566         ...
2567     }],
2568     listeners: {
2569         change: function() {
2570             // Title goes red if form has been modified.
2571             myForm.header.setStyle('color', 'red');
2572         }
2573     }
2574 });
2575 </code></pre>
2576          * @param {String/Array} events The event name to bubble, or an Array of event names.
2577          */
2578         enableBubble : function(events){
2579             var me = this;
2580             if(!Ext.isEmpty(events)){
2581                 events = Ext.isArray(events) ? events : Ext.toArray(arguments);
2582                 Ext.each(events, function(ename){
2583                     ename = ename.toLowerCase();
2584                     var ce = me.events[ename] || true;
2585                     if (Ext.isBoolean(ce)) {
2586                         ce = new Ext.util.Event(me, ename);
2587                         me.events[ename] = ce;
2588                     }
2589                     ce.bubble = true;
2590                 });
2591             }
2592         }
2593     };
2594 }());
2595
2596
2597 /**
2598  * Starts capture on the specified Observable. All events will be passed
2599  * to the supplied function with the event name + standard signature of the event
2600  * <b>before</b> the event is fired. If the supplied function returns false,
2601  * the event will not fire.
2602  * @param {Observable} o The Observable to capture events from.
2603  * @param {Function} fn The function to call when an event is fired.
2604  * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the Observable firing the event.
2605  * @static
2606  */
2607 Ext.util.Observable.capture = function(o, fn, scope){
2608     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
2609 };
2610
2611
2612 /**
2613  * Sets observability on the passed class constructor.<p>
2614  * <p>This makes any event fired on any instance of the passed class also fire a single event through
2615  * the <i>class</i> allowing for central handling of events on many instances at once.</p>
2616  * <p>Usage:</p><pre><code>
2617 Ext.util.Observable.observeClass(Ext.data.Connection);
2618 Ext.data.Connection.on('beforerequest', function(con, options) {
2619     console.log('Ajax request made to ' + options.url);
2620 });</code></pre>
2621  * @param {Function} c The class constructor to make observable.
2622  * @param {Object} listeners An object containing a series of listeners to add. See {@link #addListener}. 
2623  * @static
2624  */
2625 Ext.util.Observable.observeClass = function(c, listeners){
2626     if(c){
2627       if(!c.fireEvent){
2628           Ext.apply(c, new Ext.util.Observable());
2629           Ext.util.Observable.capture(c.prototype, c.fireEvent, c);
2630       }
2631       if(Ext.isObject(listeners)){
2632           c.on(listeners);
2633       }
2634       return c;
2635    }
2636 };/**
2637  * @class Ext.EventManager
2638  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides
2639  * several useful events directly.
2640  * See {@link Ext.EventObject} for more details on normalized event objects.
2641  * @singleton
2642  */
2643
2644 Ext.EventManager = function(){
2645     var docReadyEvent,
2646         docReadyProcId,
2647         docReadyState = false,
2648         DETECT_NATIVE = Ext.isGecko || Ext.isWebKit || Ext.isSafari,
2649         E = Ext.lib.Event,
2650         D = Ext.lib.Dom,
2651         DOC = document,
2652         WINDOW = window,
2653         DOMCONTENTLOADED = "DOMContentLoaded",
2654         COMPLETE = 'complete',
2655         propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/,
2656         /*
2657          * This cache is used to hold special js objects, the document and window, that don't have an id. We need to keep
2658          * a reference to them so we can look them up at a later point.
2659          */
2660         specialElCache = [];
2661
2662      function getId(el){
2663         var id = false,
2664             i = 0,
2665             len = specialElCache.length,
2666             id = false,
2667             skip = false,
2668             o;
2669         if(el){
2670             if(el.getElementById || el.navigator){
2671                 // look up the id
2672                 for(; i < len; ++i){
2673                     o = specialElCache[i];
2674                     if(o.el === el){
2675                         id = o.id;
2676                         break;
2677                     }
2678                 }
2679                 if(!id){
2680                     // for browsers that support it, ensure that give the el the same id
2681                     id = Ext.id(el);
2682                     specialElCache.push({
2683                         id: id,
2684                         el: el
2685                     });
2686                     skip = true;
2687                 }
2688             }else{
2689                 id = Ext.id(el);
2690             }
2691             if(!Ext.elCache[id]){
2692                 Ext.Element.addToCache(new Ext.Element(el), id);
2693                 if(skip){
2694                     Ext.elCache[id].skipGC = true;
2695                 }
2696             }
2697         }
2698         return id;
2699      };
2700
2701     /// There is some jquery work around stuff here that isn't needed in Ext Core.
2702     function addListener(el, ename, fn, task, wrap, scope){
2703         el = Ext.getDom(el);
2704         var id = getId(el),
2705             es = Ext.elCache[id].events,
2706             wfn;
2707
2708         wfn = E.on(el, ename, wrap);
2709         es[ename] = es[ename] || [];
2710
2711         /* 0 = Original Function,
2712            1 = Event Manager Wrapped Function,
2713            2 = Scope,
2714            3 = Adapter Wrapped Function,
2715            4 = Buffered Task
2716         */
2717         es[ename].push([fn, wrap, scope, wfn, task]);
2718
2719         // this is a workaround for jQuery and should somehow be removed from Ext Core in the future
2720         // without breaking ExtJS.
2721
2722         // workaround for jQuery
2723         if(el.addEventListener && ename == "mousewheel"){
2724             var args = ["DOMMouseScroll", wrap, false];
2725             el.addEventListener.apply(el, args);
2726             Ext.EventManager.addListener(WINDOW, 'unload', function(){
2727                 el.removeEventListener.apply(el, args);
2728             });
2729         }
2730
2731         // fix stopped mousedowns on the document
2732         if(el == DOC && ename == "mousedown"){
2733             Ext.EventManager.stoppedMouseDownEvent.addListener(wrap);
2734         }
2735     };
2736
2737     function doScrollChk(){
2738         /* Notes:
2739              'doScroll' will NOT work in a IFRAME/FRAMESET.
2740              The method succeeds but, a DOM query done immediately after -- FAILS.
2741           */
2742         if(window != top){
2743             return false;
2744         }
2745
2746         try{
2747             DOC.documentElement.doScroll('left');
2748         }catch(e){
2749              return false;
2750         }
2751
2752         fireDocReady();
2753         return true;
2754     }
2755     /**
2756      * @return {Boolean} True if the document is in a 'complete' state (or was determined to
2757      * be true by other means). If false, the state is evaluated again until canceled.
2758      */
2759     function checkReadyState(e){
2760
2761         if(Ext.isIE && doScrollChk()){
2762             return true;
2763         }
2764         if(DOC.readyState == COMPLETE){
2765             fireDocReady();
2766             return true;
2767         }
2768         docReadyState || (docReadyProcId = setTimeout(arguments.callee, 2));
2769         return false;
2770     }
2771
2772     var styles;
2773     function checkStyleSheets(e){
2774         styles || (styles = Ext.query('style, link[rel=stylesheet]'));
2775         if(styles.length == DOC.styleSheets.length){
2776             fireDocReady();
2777             return true;
2778         }
2779         docReadyState || (docReadyProcId = setTimeout(arguments.callee, 2));
2780         return false;
2781     }
2782
2783     function OperaDOMContentLoaded(e){
2784         DOC.removeEventListener(DOMCONTENTLOADED, arguments.callee, false);
2785         checkStyleSheets();
2786     }
2787
2788     function fireDocReady(e){
2789         if(!docReadyState){
2790             docReadyState = true; //only attempt listener removal once
2791
2792             if(docReadyProcId){
2793                 clearTimeout(docReadyProcId);
2794             }
2795             if(DETECT_NATIVE) {
2796                 DOC.removeEventListener(DOMCONTENTLOADED, fireDocReady, false);
2797             }
2798             if(Ext.isIE && checkReadyState.bindIE){  //was this was actually set ??
2799                 DOC.detachEvent('onreadystatechange', checkReadyState);
2800             }
2801             E.un(WINDOW, "load", arguments.callee);
2802         }
2803         if(docReadyEvent && !Ext.isReady){
2804             Ext.isReady = true;
2805             docReadyEvent.fire();
2806             docReadyEvent.listeners = [];
2807         }
2808
2809     };
2810
2811     function initDocReady(){
2812         docReadyEvent || (docReadyEvent = new Ext.util.Event());
2813         if (DETECT_NATIVE) {
2814             DOC.addEventListener(DOMCONTENTLOADED, fireDocReady, false);
2815         }
2816         /*
2817          * Handle additional (exceptional) detection strategies here
2818          */
2819         if (Ext.isIE){
2820             //Use readystatechange as a backup AND primary detection mechanism for a FRAME/IFRAME
2821             //See if page is already loaded
2822             if(!checkReadyState()){
2823                 checkReadyState.bindIE = true;
2824                 DOC.attachEvent('onreadystatechange', checkReadyState);
2825             }
2826
2827         }else if(Ext.isOpera ){
2828             /* Notes:
2829                Opera needs special treatment needed here because CSS rules are NOT QUITE
2830                available after DOMContentLoaded is raised.
2831             */
2832
2833             //See if page is already loaded and all styleSheets are in place
2834             (DOC.readyState == COMPLETE && checkStyleSheets()) ||
2835                 DOC.addEventListener(DOMCONTENTLOADED, OperaDOMContentLoaded, false);
2836
2837         }else if (Ext.isWebKit){
2838             //Fallback for older Webkits without DOMCONTENTLOADED support
2839             checkReadyState();
2840         }
2841         // no matter what, make sure it fires on load
2842         E.on(WINDOW, "load", fireDocReady);
2843     };
2844
2845     function createTargeted(h, o){
2846         return function(){
2847             var args = Ext.toArray(arguments);
2848             if(o.target == Ext.EventObject.setEvent(args[0]).target){
2849                 h.apply(this, args);
2850             }
2851         };
2852     };
2853
2854     function createBuffered(h, o, task){
2855         return function(e){
2856             // create new event object impl so new events don't wipe out properties
2857             task.delay(o.buffer, h, null, [new Ext.EventObjectImpl(e)]);
2858         };
2859     };
2860
2861     function createSingle(h, el, ename, fn, scope){
2862         return function(e){
2863             Ext.EventManager.removeListener(el, ename, fn, scope);
2864             h(e);
2865         };
2866     };
2867
2868     function createDelayed(h, o, fn){
2869         return function(e){
2870             var task = new Ext.util.DelayedTask(h);
2871             if(!fn.tasks) {
2872                 fn.tasks = [];
2873             }
2874             fn.tasks.push(task);
2875             task.delay(o.delay || 10, h, null, [new Ext.EventObjectImpl(e)]);
2876         };
2877     };
2878
2879     function listen(element, ename, opt, fn, scope){
2880         var o = !Ext.isObject(opt) ? {} : opt,
2881             el = Ext.getDom(element), task;
2882
2883         fn = fn || o.fn;
2884         scope = scope || o.scope;
2885
2886         if(!el){
2887             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
2888         }
2889         function h(e){
2890             // prevent errors while unload occurring
2891             if(!Ext){// !window[xname]){  ==> can't we do this?
2892                 return;
2893             }
2894             e = Ext.EventObject.setEvent(e);
2895             var t;
2896             if (o.delegate) {
2897                 if(!(t = e.getTarget(o.delegate, el))){
2898                     return;
2899                 }
2900             } else {
2901                 t = e.target;
2902             }
2903             if (o.stopEvent) {
2904                 e.stopEvent();
2905             }
2906             if (o.preventDefault) {
2907                e.preventDefault();
2908             }
2909             if (o.stopPropagation) {
2910                 e.stopPropagation();
2911             }
2912             if (o.normalized) {
2913                 e = e.browserEvent;
2914             }
2915
2916             fn.call(scope || el, e, t, o);
2917         };
2918         if(o.target){
2919             h = createTargeted(h, o);
2920         }
2921         if(o.delay){
2922             h = createDelayed(h, o, fn);
2923         }
2924         if(o.single){
2925             h = createSingle(h, el, ename, fn, scope);
2926         }
2927         if(o.buffer){
2928             task = new Ext.util.DelayedTask(h);
2929             h = createBuffered(h, o, task);
2930         }
2931
2932         addListener(el, ename, fn, task, h, scope);
2933         return h;
2934     };
2935
2936     var pub = {
2937         /**
2938          * Appends an event handler to an element.  The shorthand version {@link #on} is equivalent.  Typically you will
2939          * use {@link Ext.Element#addListener} directly on an Element in favor of calling this version.
2940          * @param {String/HTMLElement} el The html element or id to assign the event handler to.
2941          * @param {String} eventName The name of the event to listen for.
2942          * @param {Function} handler The handler function the event invokes. This function is passed
2943          * the following parameters:<ul>
2944          * <li>evt : EventObject<div class="sub-desc">The {@link Ext.EventObject EventObject} describing the event.</div></li>
2945          * <li>t : Element<div class="sub-desc">The {@link Ext.Element Element} which was the target of the event.
2946          * Note that this may be filtered by using the <tt>delegate</tt> option.</div></li>
2947          * <li>o : Object<div class="sub-desc">The options object from the addListener call.</div></li>
2948          * </ul>
2949          * @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>.
2950          * @param {Object} options (optional) An object containing handler configuration properties.
2951          * This may contain any of the following properties:<ul>
2952          * <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>
2953          * <li>delegate : String<div class="sub-desc">A simple selector to filter the target or look for a descendant of the target</div></li>
2954          * <li>stopEvent : Boolean<div class="sub-desc">True to stop the event. That is stop propagation, and prevent the default action.</div></li>
2955          * <li>preventDefault : Boolean<div class="sub-desc">True to prevent the default action</div></li>
2956          * <li>stopPropagation : Boolean<div class="sub-desc">True to prevent event propagation</div></li>
2957          * <li>normalized : Boolean<div class="sub-desc">False to pass a browser event to the handler function instead of an Ext.EventObject</div></li>
2958          * <li>delay : Number<div class="sub-desc">The number of milliseconds to delay the invocation of the handler after te event fires.</div></li>
2959          * <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>
2960          * <li>buffer : Number<div class="sub-desc">Causes the handler to be scheduled to run in an {@link Ext.util.DelayedTask} delayed
2961          * by the specified number of milliseconds. If the event fires again within that time, the original
2962          * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</div></li>
2963          * <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>
2964          * </ul><br>
2965          * <p>See {@link Ext.Element#addListener} for examples of how to use these options.</p>
2966          */
2967         addListener : function(element, eventName, fn, scope, options){
2968             if(Ext.isObject(eventName)){
2969                 var o = eventName, e, val;
2970                 for(e in o){
2971                     val = o[e];
2972                     if(!propRe.test(e)){
2973                         if(Ext.isFunction(val)){
2974                             // shared options
2975                             listen(element, e, o, val, o.scope);
2976                         }else{
2977                             // individual options
2978                             listen(element, e, val);
2979                         }
2980                     }
2981                 }
2982             } else {
2983                 listen(element, eventName, options, fn, scope);
2984             }
2985         },
2986
2987         /**
2988          * Removes an event handler from an element.  The shorthand version {@link #un} is equivalent.  Typically
2989          * you will use {@link Ext.Element#removeListener} directly on an Element in favor of calling this version.
2990          * @param {String/HTMLElement} el The id or html element from which to remove the listener.
2991          * @param {String} eventName The name of the event.
2992          * @param {Function} fn The handler function to remove. <b>This must be a reference to the function passed into the {@link #addListener} call.</b>
2993          * @param {Object} scope If a scope (<b><code>this</code></b> reference) was specified when the listener was added,
2994          * then this must refer to the same object.
2995          */
2996         removeListener : function(el, eventName, fn, scope){
2997             el = Ext.getDom(el);
2998             var id = getId(el),
2999                 f = el && (Ext.elCache[id].events)[eventName] || [],
3000                 wrap, i, l, k, len, fnc;
3001
3002             for (i = 0, len = f.length; i < len; i++) {
3003
3004                 /* 0 = Original Function,
3005                    1 = Event Manager Wrapped Function,
3006                    2 = Scope,
3007                    3 = Adapter Wrapped Function,
3008                    4 = Buffered Task
3009                 */
3010                 if (Ext.isArray(fnc = f[i]) && fnc[0] == fn && (!scope || fnc[2] == scope)) {
3011                     if(fnc[4]) {
3012                         fnc[4].cancel();
3013                     }
3014                     k = fn.tasks && fn.tasks.length;
3015                     if(k) {
3016                         while(k--) {
3017                             fn.tasks[k].cancel();
3018                         }
3019                         delete fn.tasks;
3020                     }
3021                     wrap = fnc[1];
3022                     E.un(el, eventName, E.extAdapter ? fnc[3] : wrap);
3023
3024                     // jQuery workaround that should be removed from Ext Core
3025                     if(wrap && el.addEventListener && eventName == "mousewheel"){
3026                         el.removeEventListener("DOMMouseScroll", wrap, false);
3027                     }
3028
3029                     // fix stopped mousedowns on the document
3030                     if(wrap && el == DOC && eventName == "mousedown"){
3031                         Ext.EventManager.stoppedMouseDownEvent.removeListener(wrap);
3032                     }
3033
3034                     f.splice(i, 1);
3035                     if (f.length === 0) {
3036                         delete Ext.elCache[id].events[eventName];
3037                     }
3038                     for (k in Ext.elCache[id].events) {
3039                         return false;
3040                     }
3041                     Ext.elCache[id].events = {};
3042                     return false;
3043                 }
3044             }
3045         },
3046
3047         /**
3048          * Removes all event handers from an element.  Typically you will use {@link Ext.Element#removeAllListeners}
3049          * directly on an Element in favor of calling this version.
3050          * @param {String/HTMLElement} el The id or html element from which to remove all event handlers.
3051          */
3052         removeAll : function(el){
3053             el = Ext.getDom(el);
3054             var id = getId(el),
3055                 ec = Ext.elCache[id] || {},
3056                 es = ec.events || {},
3057                 f, i, len, ename, fn, k, wrap;
3058
3059             for(ename in es){
3060                 if(es.hasOwnProperty(ename)){
3061                     f = es[ename];
3062                     /* 0 = Original Function,
3063                        1 = Event Manager Wrapped Function,
3064                        2 = Scope,
3065                        3 = Adapter Wrapped Function,
3066                        4 = Buffered Task
3067                     */
3068                     for (i = 0, len = f.length; i < len; i++) {
3069                         fn = f[i];
3070                         if(fn[4]) {
3071                             fn[4].cancel();
3072                         }
3073                         if(fn[0].tasks && (k = fn[0].tasks.length)) {
3074                             while(k--) {
3075                                 fn[0].tasks[k].cancel();
3076                             }
3077                             delete fn.tasks;
3078                         }
3079                         wrap =  fn[1];
3080                         E.un(el, ename, E.extAdapter ? fn[3] : wrap);
3081
3082                         // jQuery workaround that should be removed from Ext Core
3083                         if(el.addEventListener && wrap && ename == "mousewheel"){
3084                             el.removeEventListener("DOMMouseScroll", wrap, false);
3085                         }
3086
3087                         // fix stopped mousedowns on the document
3088                         if(wrap && el == DOC &&  ename == "mousedown"){
3089                             Ext.EventManager.stoppedMouseDownEvent.removeListener(wrap);
3090                         }
3091                     }
3092                 }
3093             }
3094             if (Ext.elCache[id]) {
3095                 Ext.elCache[id].events = {};
3096             }
3097         },
3098
3099         getListeners : function(el, eventName) {
3100             el = Ext.getDom(el);
3101             var id = getId(el),
3102                 ec = Ext.elCache[id] || {},
3103                 es = ec.events || {},
3104                 results = [];
3105             if (es && es[eventName]) {
3106                 return es[eventName];
3107             } else {
3108                 return null;
3109             }
3110         },
3111
3112         purgeElement : function(el, recurse, eventName) {
3113             el = Ext.getDom(el);
3114             var id = getId(el),
3115                 ec = Ext.elCache[id] || {},
3116                 es = ec.events || {},
3117                 i, f, len;
3118             if (eventName) {
3119                 if (es && es.hasOwnProperty(eventName)) {
3120                     f = es[eventName];
3121                     for (i = 0, len = f.length; i < len; i++) {
3122                         Ext.EventManager.removeListener(el, eventName, f[i][0]);
3123                     }
3124                 }
3125             } else {
3126                 Ext.EventManager.removeAll(el);
3127             }
3128             if (recurse && el && el.childNodes) {
3129                 for (i = 0, len = el.childNodes.length; i < len; i++) {
3130                     Ext.EventManager.purgeElement(el.childNodes[i], recurse, eventName);
3131                 }
3132             }
3133         },
3134
3135         _unload : function() {
3136             var el;
3137             for (el in Ext.elCache) {
3138                 Ext.EventManager.removeAll(el);
3139             }
3140             delete Ext.elCache;
3141             delete Ext.Element._flyweights;
3142
3143             // Abort any outstanding Ajax requests
3144             var c,
3145                 conn,
3146                 tid,
3147                 ajax = Ext.lib.Ajax;
3148             (Ext.isObject(ajax.conn)) ? conn = ajax.conn : conn = {};
3149             for (tid in conn) {
3150                 c = conn[tid];
3151                 if (c) {
3152                     ajax.abort({conn: c, tId: tid});
3153                 }
3154             }
3155         },
3156         /**
3157          * Adds a listener to be notified when the document is ready (before onload and before images are loaded). Can be
3158          * accessed shorthanded as Ext.onReady().
3159          * @param {Function} fn The method the event invokes.
3160          * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the handler function executes. Defaults to the browser window.
3161          * @param {boolean} options (optional) Options object as passed to {@link Ext.Element#addListener}. It is recommended that the options
3162          * <code>{single: true}</code> be used so that the handler is removed on first invocation.
3163          */
3164         onDocumentReady : function(fn, scope, options){
3165             if(Ext.isReady){ // if it already fired or document.body is present
3166                 docReadyEvent || (docReadyEvent = new Ext.util.Event());
3167                 docReadyEvent.addListener(fn, scope, options);
3168                 docReadyEvent.fire();
3169                 docReadyEvent.listeners = [];
3170             }else{
3171                 if(!docReadyEvent){
3172                     initDocReady();
3173                 }
3174                 options = options || {};
3175                 options.delay = options.delay || 1;
3176                 docReadyEvent.addListener(fn, scope, options);
3177             }
3178         },
3179
3180         /**
3181          * Forces a document ready state transition for the framework.  Used when Ext is loaded
3182          * into a DOM structure AFTER initial page load (Google API or other dynamic load scenario.
3183          * Any pending 'onDocumentReady' handlers will be fired (if not already handled).
3184          */
3185         fireDocReady  : fireDocReady
3186     };
3187      /**
3188      * Appends an event handler to an element.  Shorthand for {@link #addListener}.
3189      * @param {String/HTMLElement} el The html element or id to assign the event handler to
3190      * @param {String} eventName The name of the event to listen for.
3191      * @param {Function} handler The handler function the event invokes.
3192      * @param {Object} scope (optional) (<code>this</code> reference) in which the handler function executes. <b>Defaults to the Element</b>.
3193      * @param {Object} options (optional) An object containing standard {@link #addListener} options
3194      * @member Ext.EventManager
3195      * @method on
3196      */
3197     pub.on = pub.addListener;
3198     /**
3199      * Removes an event handler from an element.  Shorthand for {@link #removeListener}.
3200      * @param {String/HTMLElement} el The id or html element from which to remove the listener.
3201      * @param {String} eventName The name of the event.
3202      * @param {Function} fn The handler function to remove. <b>This must be a reference to the function passed into the {@link #on} call.</b>
3203      * @param {Object} scope If a scope (<b><code>this</code></b> reference) was specified when the listener was added,
3204      * then this must refer to the same object.
3205      * @member Ext.EventManager
3206      * @method un
3207      */
3208     pub.un = pub.removeListener;
3209
3210     pub.stoppedMouseDownEvent = new Ext.util.Event();
3211     return pub;
3212 }();
3213 /**
3214   * Adds a listener to be notified when the document is ready (before onload and before images are loaded). Shorthand of {@link Ext.EventManager#onDocumentReady}.
3215   * @param {Function} fn The method the event invokes.
3216   * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the handler function executes. Defaults to the browser window.
3217   * @param {boolean} options (optional) Options object as passed to {@link Ext.Element#addListener}. It is recommended that the options
3218   * <code>{single: true}</code> be used so that the handler is removed on first invocation.
3219   * @member Ext
3220   * @method onReady
3221  */
3222 Ext.onReady = Ext.EventManager.onDocumentReady;
3223
3224
3225 //Initialize doc classes
3226 (function(){
3227
3228     var initExtCss = function(){
3229         // find the body element
3230         var bd = document.body || document.getElementsByTagName('body')[0];
3231         if(!bd){ return false; }
3232         var cls = [' ',
3233                 Ext.isIE ? "ext-ie " + (Ext.isIE6 ? 'ext-ie6' : (Ext.isIE7 ? 'ext-ie7' : 'ext-ie8'))
3234                 : Ext.isGecko ? "ext-gecko " + (Ext.isGecko2 ? 'ext-gecko2' : 'ext-gecko3')
3235                 : Ext.isOpera ? "ext-opera"
3236                 : Ext.isWebKit ? "ext-webkit" : ""];
3237
3238         if(Ext.isSafari){
3239             cls.push("ext-safari " + (Ext.isSafari2 ? 'ext-safari2' : (Ext.isSafari3 ? 'ext-safari3' : 'ext-safari4')));
3240         }else if(Ext.isChrome){
3241             cls.push("ext-chrome");
3242         }
3243
3244         if(Ext.isMac){
3245             cls.push("ext-mac");
3246         }
3247         if(Ext.isLinux){
3248             cls.push("ext-linux");
3249         }
3250
3251         if(Ext.isStrict || Ext.isBorderBox){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
3252             var p = bd.parentNode;
3253             if(p){
3254                 p.className += Ext.isStrict ? ' ext-strict' : ' ext-border-box';
3255             }
3256         }
3257         bd.className += cls.join(' ');
3258         return true;
3259     }
3260
3261     if(!initExtCss()){
3262         Ext.onReady(initExtCss);
3263     }
3264 })();
3265
3266
3267 /**
3268  * @class Ext.EventObject
3269  * Just as {@link Ext.Element} wraps around a native DOM node, Ext.EventObject
3270  * wraps the browser's native event-object normalizing cross-browser differences,
3271  * such as which mouse button is clicked, keys pressed, mechanisms to stop
3272  * event-propagation along with a method to prevent default actions from taking place.
3273  * <p>For example:</p>
3274  * <pre><code>
3275 function handleClick(e, t){ // e is not a standard event object, it is a Ext.EventObject
3276     e.preventDefault();
3277     var target = e.getTarget(); // same as t (the target HTMLElement)
3278     ...
3279 }
3280 var myDiv = {@link Ext#get Ext.get}("myDiv");  // get reference to an {@link Ext.Element}
3281 myDiv.on(         // 'on' is shorthand for addListener
3282     "click",      // perform an action on click of myDiv
3283     handleClick   // reference to the action handler
3284 );
3285 // other methods to do the same:
3286 Ext.EventManager.on("myDiv", 'click', handleClick);
3287 Ext.EventManager.addListener("myDiv", 'click', handleClick);
3288  </code></pre>
3289  * @singleton
3290  */
3291 Ext.EventObject = function(){
3292     var E = Ext.lib.Event,
3293         // safari keypress events for special keys return bad keycodes
3294         safariKeys = {
3295             3 : 13, // enter
3296             63234 : 37, // left
3297             63235 : 39, // right
3298             63232 : 38, // up
3299             63233 : 40, // down
3300             63276 : 33, // page up
3301             63277 : 34, // page down
3302             63272 : 46, // delete
3303             63273 : 36, // home
3304             63275 : 35  // end
3305         },
3306         // normalize button clicks
3307         btnMap = Ext.isIE ? {1:0,4:1,2:2} :
3308                 (Ext.isWebKit ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
3309
3310     Ext.EventObjectImpl = function(e){
3311         if(e){
3312             this.setEvent(e.browserEvent || e);
3313         }
3314     };
3315
3316     Ext.EventObjectImpl.prototype = {
3317            /** @private */
3318         setEvent : function(e){
3319             var me = this;
3320             if(e == me || (e && e.browserEvent)){ // already wrapped
3321                 return e;
3322             }
3323             me.browserEvent = e;
3324             if(e){
3325                 // normalize buttons
3326                 me.button = e.button ? btnMap[e.button] : (e.which ? e.which - 1 : -1);
3327                 if(e.type == 'click' && me.button == -1){
3328                     me.button = 0;
3329                 }
3330                 me.type = e.type;
3331                 me.shiftKey = e.shiftKey;
3332                 // mac metaKey behaves like ctrlKey
3333                 me.ctrlKey = e.ctrlKey || e.metaKey || false;
3334                 me.altKey = e.altKey;
3335                 // in getKey these will be normalized for the mac
3336                 me.keyCode = e.keyCode;
3337                 me.charCode = e.charCode;
3338                 // cache the target for the delayed and or buffered events
3339                 me.target = E.getTarget(e);
3340                 // same for XY
3341                 me.xy = E.getXY(e);
3342             }else{
3343                 me.button = -1;
3344                 me.shiftKey = false;
3345                 me.ctrlKey = false;
3346                 me.altKey = false;
3347                 me.keyCode = 0;
3348                 me.charCode = 0;
3349                 me.target = null;
3350                 me.xy = [0, 0];
3351             }
3352             return me;
3353         },
3354
3355         /**
3356          * Stop the event (preventDefault and stopPropagation)
3357          */
3358         stopEvent : function(){
3359             var me = this;
3360             if(me.browserEvent){
3361                 if(me.browserEvent.type == 'mousedown'){
3362                     Ext.EventManager.stoppedMouseDownEvent.fire(me);
3363                 }
3364                 E.stopEvent(me.browserEvent);
3365             }
3366         },
3367
3368         /**
3369          * Prevents the browsers default handling of the event.
3370          */
3371         preventDefault : function(){
3372             if(this.browserEvent){
3373                 E.preventDefault(this.browserEvent);
3374             }
3375         },
3376
3377         /**
3378          * Cancels bubbling of the event.
3379          */
3380         stopPropagation : function(){
3381             var me = this;
3382             if(me.browserEvent){
3383                 if(me.browserEvent.type == 'mousedown'){
3384                     Ext.EventManager.stoppedMouseDownEvent.fire(me);
3385                 }
3386                 E.stopPropagation(me.browserEvent);
3387             }
3388         },
3389
3390         /**
3391          * Gets the character code for the event.
3392          * @return {Number}
3393          */
3394         getCharCode : function(){
3395             return this.charCode || this.keyCode;
3396         },
3397
3398         /**
3399          * Returns a normalized keyCode for the event.
3400          * @return {Number} The key code
3401          */
3402         getKey : function(){
3403             return this.normalizeKey(this.keyCode || this.charCode)
3404         },
3405
3406         // private
3407         normalizeKey: function(k){
3408             return Ext.isSafari ? (safariKeys[k] || k) : k;
3409         },
3410
3411         /**
3412          * Gets the x coordinate of the event.
3413          * @return {Number}
3414          */
3415         getPageX : function(){
3416             return this.xy[0];
3417         },
3418
3419         /**
3420          * Gets the y coordinate of the event.
3421          * @return {Number}
3422          */
3423         getPageY : function(){
3424             return this.xy[1];
3425         },
3426
3427         /**
3428          * Gets the page coordinates of the event.
3429          * @return {Array} The xy values like [x, y]
3430          */
3431         getXY : function(){
3432             return this.xy;
3433         },
3434
3435         /**
3436          * Gets the target for the event.
3437          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
3438          * @param {Number/Mixed} maxDepth (optional) The max depth to
3439                 search as a number or element (defaults to 10 || document.body)
3440          * @param {Boolean} returnEl (optional) True to return a Ext.Element object instead of DOM node
3441          * @return {HTMLelement}
3442          */
3443         getTarget : function(selector, maxDepth, returnEl){
3444             return selector ? Ext.fly(this.target).findParent(selector, maxDepth, returnEl) : (returnEl ? Ext.get(this.target) : this.target);
3445         },
3446
3447         /**
3448          * Gets the related target.
3449          * @return {HTMLElement}
3450          */
3451         getRelatedTarget : function(){
3452             return this.browserEvent ? E.getRelatedTarget(this.browserEvent) : null;
3453         },
3454
3455         /**
3456          * Normalizes mouse wheel delta across browsers
3457          * @return {Number} The delta
3458          */
3459         getWheelDelta : function(){
3460             var e = this.browserEvent;
3461             var delta = 0;
3462             if(e.wheelDelta){ /* IE/Opera. */
3463                 delta = e.wheelDelta/120;
3464             }else if(e.detail){ /* Mozilla case. */
3465                 delta = -e.detail/3;
3466             }
3467             return delta;
3468         },
3469
3470         /**
3471         * 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.
3472         * Example usage:<pre><code>
3473         // Handle click on any child of an element
3474         Ext.getBody().on('click', function(e){
3475             if(e.within('some-el')){
3476                 alert('Clicked on a child of some-el!');
3477             }
3478         });
3479
3480         // Handle click directly on an element, ignoring clicks on child nodes
3481         Ext.getBody().on('click', function(e,t){
3482             if((t.id == 'some-el') && !e.within(t, true)){
3483                 alert('Clicked directly on some-el!');
3484             }
3485         });
3486         </code></pre>
3487          * @param {Mixed} el The id, DOM element or Ext.Element to check
3488          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
3489          * @param {Boolean} allowEl {optional} true to also check if the passed element is the target or related target
3490          * @return {Boolean}
3491          */
3492         within : function(el, related, allowEl){
3493             if(el){
3494                 var t = this[related ? "getRelatedTarget" : "getTarget"]();
3495                 return t && ((allowEl ? (t == Ext.getDom(el)) : false) || Ext.fly(el).contains(t));
3496             }
3497             return false;
3498         }
3499      };
3500
3501     return new Ext.EventObjectImpl();
3502 }();
3503 /**
3504 * @class Ext.EventManager
3505 */
3506 Ext.apply(Ext.EventManager, function(){
3507    var resizeEvent,
3508        resizeTask,
3509        textEvent,
3510        textSize,
3511        D = Ext.lib.Dom,
3512        propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/,
3513        curWidth = 0,
3514        curHeight = 0,
3515        // note 1: IE fires ONLY the keydown event on specialkey autorepeat
3516        // note 2: Safari < 3.1, Gecko (Mac/Linux) & Opera fire only the keypress event on specialkey autorepeat
3517        // (research done by @Jan Wolter at http://unixpapa.com/js/key.html)
3518        useKeydown = Ext.isWebKit ?
3519                    Ext.num(navigator.userAgent.match(/AppleWebKit\/(\d+)/)[1]) >= 525 :
3520                    !((Ext.isGecko && !Ext.isWindows) || Ext.isOpera);
3521
3522    return {
3523        // private
3524        doResizeEvent: function(){
3525            var h = D.getViewHeight(),
3526                w = D.getViewWidth();
3527
3528             //whacky problem in IE where the resize event will fire even though the w/h are the same.
3529             if(curHeight != h || curWidth != w){
3530                resizeEvent.fire(curWidth = w, curHeight = h);
3531             }
3532        },
3533
3534        /**
3535         * Adds a listener to be notified when the browser window is resized and provides resize event buffering (100 milliseconds),
3536         * passes new viewport width and height to handlers.
3537         * @param {Function} fn      The handler function the window resize event invokes.
3538         * @param {Object}   scope   The scope (<code>this</code> reference) in which the handler function executes. Defaults to the browser window.
3539         * @param {boolean}  options Options object as passed to {@link Ext.Element#addListener}
3540         */
3541        onWindowResize : function(fn, scope, options){
3542            if(!resizeEvent){
3543                resizeEvent = new Ext.util.Event();
3544                resizeTask = new Ext.util.DelayedTask(this.doResizeEvent);
3545                Ext.EventManager.on(window, "resize", this.fireWindowResize, this);
3546            }
3547            resizeEvent.addListener(fn, scope, options);
3548        },
3549
3550        // exposed only to allow manual firing
3551        fireWindowResize : function(){
3552            if(resizeEvent){
3553                resizeTask.delay(100);
3554            }
3555        },
3556
3557        /**
3558         * 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.
3559         * @param {Function} fn      The function the event invokes.
3560         * @param {Object}   scope   The scope (<code>this</code> reference) in which the handler function executes. Defaults to the browser window.
3561         * @param {boolean}  options Options object as passed to {@link Ext.Element#addListener}
3562         */
3563        onTextResize : function(fn, scope, options){
3564            if(!textEvent){
3565                textEvent = new Ext.util.Event();
3566                var textEl = new Ext.Element(document.createElement('div'));
3567                textEl.dom.className = 'x-text-resize';
3568                textEl.dom.innerHTML = 'X';
3569                textEl.appendTo(document.body);
3570                textSize = textEl.dom.offsetHeight;
3571                setInterval(function(){
3572                    if(textEl.dom.offsetHeight != textSize){
3573                        textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
3574                    }
3575                }, this.textResizeInterval);
3576            }
3577            textEvent.addListener(fn, scope, options);
3578        },
3579
3580        /**
3581         * Removes the passed window resize listener.
3582         * @param {Function} fn        The method the event invokes
3583         * @param {Object}   scope    The scope of handler
3584         */
3585        removeResizeListener : function(fn, scope){
3586            if(resizeEvent){
3587                resizeEvent.removeListener(fn, scope);
3588            }
3589        },
3590
3591        // private
3592        fireResize : function(){
3593            if(resizeEvent){
3594                resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
3595            }
3596        },
3597
3598         /**
3599         * The frequency, in milliseconds, to check for text resize events (defaults to 50)
3600         */
3601        textResizeInterval : 50,
3602
3603        /**
3604         * Url used for onDocumentReady with using SSL (defaults to Ext.SSL_SECURE_URL)
3605         */
3606        ieDeferSrc : false,
3607
3608        // protected for use inside the framework
3609        // detects whether we should use keydown or keypress based on the browser.
3610        useKeydown: useKeydown
3611    };
3612 }());
3613
3614 Ext.EventManager.on = Ext.EventManager.addListener;
3615
3616
3617 Ext.apply(Ext.EventObjectImpl.prototype, {
3618    /** Key constant @type Number */
3619    BACKSPACE: 8,
3620    /** Key constant @type Number */
3621    TAB: 9,
3622    /** Key constant @type Number */
3623    NUM_CENTER: 12,
3624    /** Key constant @type Number */
3625    ENTER: 13,
3626    /** Key constant @type Number */
3627    RETURN: 13,
3628    /** Key constant @type Number */
3629    SHIFT: 16,
3630    /** Key constant @type Number */
3631    CTRL: 17,
3632    CONTROL : 17, // legacy
3633    /** Key constant @type Number */
3634    ALT: 18,
3635    /** Key constant @type Number */
3636    PAUSE: 19,
3637    /** Key constant @type Number */
3638    CAPS_LOCK: 20,
3639    /** Key constant @type Number */
3640    ESC: 27,
3641    /** Key constant @type Number */
3642    SPACE: 32,
3643    /** Key constant @type Number */
3644    PAGE_UP: 33,
3645    PAGEUP : 33, // legacy
3646    /** Key constant @type Number */
3647    PAGE_DOWN: 34,
3648    PAGEDOWN : 34, // legacy
3649    /** Key constant @type Number */
3650    END: 35,
3651    /** Key constant @type Number */
3652    HOME: 36,
3653    /** Key constant @type Number */
3654    LEFT: 37,
3655    /** Key constant @type Number */
3656    UP: 38,
3657    /** Key constant @type Number */
3658    RIGHT: 39,
3659    /** Key constant @type Number */
3660    DOWN: 40,
3661    /** Key constant @type Number */
3662    PRINT_SCREEN: 44,
3663    /** Key constant @type Number */
3664    INSERT: 45,
3665    /** Key constant @type Number */
3666    DELETE: 46,
3667    /** Key constant @type Number */
3668    ZERO: 48,
3669    /** Key constant @type Number */
3670    ONE: 49,
3671    /** Key constant @type Number */
3672    TWO: 50,
3673    /** Key constant @type Number */
3674    THREE: 51,
3675    /** Key constant @type Number */
3676    FOUR: 52,
3677    /** Key constant @type Number */
3678    FIVE: 53,
3679    /** Key constant @type Number */
3680    SIX: 54,
3681    /** Key constant @type Number */
3682    SEVEN: 55,
3683    /** Key constant @type Number */
3684    EIGHT: 56,
3685    /** Key constant @type Number */
3686    NINE: 57,
3687    /** Key constant @type Number */
3688    A: 65,
3689    /** Key constant @type Number */
3690    B: 66,
3691    /** Key constant @type Number */
3692    C: 67,
3693    /** Key constant @type Number */
3694    D: 68,
3695    /** Key constant @type Number */
3696    E: 69,
3697    /** Key constant @type Number */
3698    F: 70,
3699    /** Key constant @type Number */
3700    G: 71,
3701    /** Key constant @type Number */
3702    H: 72,
3703    /** Key constant @type Number */
3704    I: 73,
3705    /** Key constant @type Number */
3706    J: 74,
3707    /** Key constant @type Number */
3708    K: 75,
3709    /** Key constant @type Number */
3710    L: 76,
3711    /** Key constant @type Number */
3712    M: 77,
3713    /** Key constant @type Number */
3714    N: 78,
3715    /** Key constant @type Number */
3716    O: 79,
3717    /** Key constant @type Number */
3718    P: 80,
3719    /** Key constant @type Number */
3720    Q: 81,
3721    /** Key constant @type Number */
3722    R: 82,
3723    /** Key constant @type Number */
3724    S: 83,
3725    /** Key constant @type Number */
3726    T: 84,
3727    /** Key constant @type Number */
3728    U: 85,
3729    /** Key constant @type Number */
3730    V: 86,
3731    /** Key constant @type Number */
3732    W: 87,
3733    /** Key constant @type Number */
3734    X: 88,
3735    /** Key constant @type Number */
3736    Y: 89,
3737    /** Key constant @type Number */
3738    Z: 90,
3739    /** Key constant @type Number */
3740    CONTEXT_MENU: 93,
3741    /** Key constant @type Number */
3742    NUM_ZERO: 96,
3743    /** Key constant @type Number */
3744    NUM_ONE: 97,
3745    /** Key constant @type Number */
3746    NUM_TWO: 98,
3747    /** Key constant @type Number */
3748    NUM_THREE: 99,
3749    /** Key constant @type Number */
3750    NUM_FOUR: 100,
3751    /** Key constant @type Number */
3752    NUM_FIVE: 101,
3753    /** Key constant @type Number */
3754    NUM_SIX: 102,
3755    /** Key constant @type Number */
3756    NUM_SEVEN: 103,
3757    /** Key constant @type Number */
3758    NUM_EIGHT: 104,
3759    /** Key constant @type Number */
3760    NUM_NINE: 105,
3761    /** Key constant @type Number */
3762    NUM_MULTIPLY: 106,
3763    /** Key constant @type Number */
3764    NUM_PLUS: 107,
3765    /** Key constant @type Number */
3766    NUM_MINUS: 109,
3767    /** Key constant @type Number */
3768    NUM_PERIOD: 110,
3769    /** Key constant @type Number */
3770    NUM_DIVISION: 111,
3771    /** Key constant @type Number */
3772    F1: 112,
3773    /** Key constant @type Number */
3774    F2: 113,
3775    /** Key constant @type Number */
3776    F3: 114,
3777    /** Key constant @type Number */
3778    F4: 115,
3779    /** Key constant @type Number */
3780    F5: 116,
3781    /** Key constant @type Number */
3782    F6: 117,
3783    /** Key constant @type Number */
3784    F7: 118,
3785    /** Key constant @type Number */
3786    F8: 119,
3787    /** Key constant @type Number */
3788    F9: 120,
3789    /** Key constant @type Number */
3790    F10: 121,
3791    /** Key constant @type Number */
3792    F11: 122,
3793    /** Key constant @type Number */
3794    F12: 123,
3795
3796    /** @private */
3797    isNavKeyPress : function(){
3798        var me = this,
3799            k = this.normalizeKey(me.keyCode);
3800        return (k >= 33 && k <= 40) ||  // Page Up/Down, End, Home, Left, Up, Right, Down
3801        k == me.RETURN ||
3802        k == me.TAB ||
3803        k == me.ESC;
3804    },
3805
3806    isSpecialKey : function(){
3807        var k = this.normalizeKey(this.keyCode);
3808        return (this.type == 'keypress' && this.ctrlKey) ||
3809        this.isNavKeyPress() ||
3810        (k == this.BACKSPACE) || // Backspace
3811        (k >= 16 && k <= 20) || // Shift, Ctrl, Alt, Pause, Caps Lock
3812        (k >= 44 && k <= 46);   // Print Screen, Insert, Delete
3813    },
3814
3815    getPoint : function(){
3816        return new Ext.lib.Point(this.xy[0], this.xy[1]);
3817    },
3818
3819    /**
3820     * Returns true if the control, meta, shift or alt key was pressed during this event.
3821     * @return {Boolean}
3822     */
3823    hasModifier : function(){
3824        return ((this.ctrlKey || this.altKey) || this.shiftKey);
3825    }
3826 });/**
3827  * @class Ext.Element
3828  * <p>Encapsulates a DOM element, adding simple DOM manipulation facilities, normalizing for browser differences.</p>
3829  * <p>All instances of this class inherit the methods of {@link Ext.Fx} making visual effects easily available to all DOM elements.</p>
3830  * <p>Note that the events documented in this class are not Ext events, they encapsulate browser events. To
3831  * access the underlying browser event, see {@link Ext.EventObject#browserEvent}. Some older
3832  * browsers may not support the full range of events. Which events are supported is beyond the control of ExtJs.</p>
3833  * Usage:<br>
3834 <pre><code>
3835 // by id
3836 var el = Ext.get("my-div");
3837
3838 // by DOM element reference
3839 var el = Ext.get(myDivElement);
3840 </code></pre>
3841  * <b>Animations</b><br />
3842  * <p>When an element is manipulated, by default there is no animation.</p>
3843  * <pre><code>
3844 var el = Ext.get("my-div");
3845
3846 // no animation
3847 el.setWidth(100);
3848  * </code></pre>
3849  * <p>Many of the functions for manipulating an element have an optional "animate" parameter.  This
3850  * parameter can be specified as boolean (<tt>true</tt>) for default animation effects.</p>
3851  * <pre><code>
3852 // default animation
3853 el.setWidth(100, true);
3854  * </code></pre>
3855  *
3856  * <p>To configure the effects, an object literal with animation options to use as the Element animation
3857  * configuration object can also be specified. Note that the supported Element animation configuration
3858  * options are a subset of the {@link Ext.Fx} animation options specific to Fx effects.  The supported
3859  * Element animation configuration options are:</p>
3860 <pre>
3861 Option    Default   Description
3862 --------- --------  ---------------------------------------------
3863 {@link Ext.Fx#duration duration}  .35       The duration of the animation in seconds
3864 {@link Ext.Fx#easing easing}    easeOut   The easing method
3865 {@link Ext.Fx#callback callback}  none      A function to execute when the anim completes
3866 {@link Ext.Fx#scope scope}     this      The scope (this) of the callback function
3867 </pre>
3868  *
3869  * <pre><code>
3870 // Element animation options object
3871 var opt = {
3872     {@link Ext.Fx#duration duration}: 1,
3873     {@link Ext.Fx#easing easing}: 'elasticIn',
3874     {@link Ext.Fx#callback callback}: this.foo,
3875     {@link Ext.Fx#scope scope}: this
3876 };
3877 // animation with some options set
3878 el.setWidth(100, opt);
3879  * </code></pre>
3880  * <p>The Element animation object being used for the animation will be set on the options
3881  * object as "anim", which allows you to stop or manipulate the animation. Here is an example:</p>
3882  * <pre><code>
3883 // using the "anim" property to get the Anim object
3884 if(opt.anim.isAnimated()){
3885     opt.anim.stop();
3886 }
3887  * </code></pre>
3888  * <p>Also see the <tt>{@link #animate}</tt> method for another animation technique.</p>
3889  * <p><b> Composite (Collections of) Elements</b></p>
3890  * <p>For working with collections of Elements, see {@link Ext.CompositeElement}</p>
3891  * @constructor Create a new Element directly.
3892  * @param {String/HTMLElement} element
3893  * @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).
3894  */
3895 (function(){
3896 var DOC = document;
3897
3898 Ext.Element = function(element, forceNew){
3899     var dom = typeof element == "string" ?
3900               DOC.getElementById(element) : element,
3901         id;
3902
3903     if(!dom) return null;
3904
3905     id = dom.id;
3906
3907     if(!forceNew && id && Ext.elCache[id]){ // element object already exists
3908         return Ext.elCache[id].el;
3909     }
3910
3911     /**
3912      * The DOM element
3913      * @type HTMLElement
3914      */
3915     this.dom = dom;
3916
3917     /**
3918      * The DOM element ID
3919      * @type String
3920      */
3921     this.id = id || Ext.id(dom);
3922 };
3923
3924 var D = Ext.lib.Dom,
3925     DH = Ext.DomHelper,
3926     E = Ext.lib.Event,
3927     A = Ext.lib.Anim,
3928     El = Ext.Element,
3929     EC = Ext.elCache;
3930
3931 El.prototype = {
3932     /**
3933      * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
3934      * @param {Object} o The object with the attributes
3935      * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
3936      * @return {Ext.Element} this
3937      */
3938     set : function(o, useSet){
3939         var el = this.dom,
3940             attr,
3941             val,
3942             useSet = (useSet !== false) && !!el.setAttribute;
3943
3944         for(attr in o){
3945             if (o.hasOwnProperty(attr)) {
3946                 val = o[attr];
3947                 if (attr == 'style') {
3948                     DH.applyStyles(el, val);
3949                 } else if (attr == 'cls') {
3950                     el.className = val;
3951                 } else if (useSet) {
3952                     el.setAttribute(attr, val);
3953                 } else {
3954                     el[attr] = val;
3955                 }
3956             }
3957         }
3958         return this;
3959     },
3960
3961 //  Mouse events
3962     /**
3963      * @event click
3964      * Fires when a mouse click is detected within the element.
3965      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
3966      * @param {HtmlElement} t The target of the event.
3967      * @param {Object} o The options configuration passed to the {@link #addListener} call.
3968      */
3969     /**
3970      * @event contextmenu
3971      * Fires when a right click is detected within the element.
3972      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
3973      * @param {HtmlElement} t The target of the event.
3974      * @param {Object} o The options configuration passed to the {@link #addListener} call.
3975      */
3976     /**
3977      * @event dblclick
3978      * Fires when a mouse double click is detected within the element.
3979      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
3980      * @param {HtmlElement} t The target of the event.
3981      * @param {Object} o The options configuration passed to the {@link #addListener} call.
3982      */
3983     /**
3984      * @event mousedown
3985      * Fires when a mousedown is detected within the element.
3986      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
3987      * @param {HtmlElement} t The target of the event.
3988      * @param {Object} o The options configuration passed to the {@link #addListener} call.
3989      */
3990     /**
3991      * @event mouseup
3992      * Fires when a mouseup is detected within the element.
3993      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
3994      * @param {HtmlElement} t The target of the event.
3995      * @param {Object} o The options configuration passed to the {@link #addListener} call.
3996      */
3997     /**
3998      * @event mouseover
3999      * Fires when a mouseover is detected within the element.
4000      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4001      * @param {HtmlElement} t The target of the event.
4002      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4003      */
4004     /**
4005      * @event mousemove
4006      * Fires when a mousemove is detected with the element.
4007      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4008      * @param {HtmlElement} t The target of the event.
4009      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4010      */
4011     /**
4012      * @event mouseout
4013      * Fires when a mouseout is detected with the element.
4014      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4015      * @param {HtmlElement} t The target of the event.
4016      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4017      */
4018     /**
4019      * @event mouseenter
4020      * Fires when the mouse enters the element.
4021      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4022      * @param {HtmlElement} t The target of the event.
4023      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4024      */
4025     /**
4026      * @event mouseleave
4027      * Fires when the mouse leaves the element.
4028      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4029      * @param {HtmlElement} t The target of the event.
4030      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4031      */
4032
4033 //  Keyboard events
4034     /**
4035      * @event keypress
4036      * Fires when a keypress is detected within the element.
4037      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4038      * @param {HtmlElement} t The target of the event.
4039      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4040      */
4041     /**
4042      * @event keydown
4043      * Fires when a keydown is detected within the element.
4044      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4045      * @param {HtmlElement} t The target of the event.
4046      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4047      */
4048     /**
4049      * @event keyup
4050      * Fires when a keyup is detected within the element.
4051      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4052      * @param {HtmlElement} t The target of the event.
4053      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4054      */
4055
4056
4057 //  HTML frame/object events
4058     /**
4059      * @event load
4060      * Fires when the user agent finishes loading all content within the element. Only supported by window, frames, objects and images.
4061      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4062      * @param {HtmlElement} t The target of the event.
4063      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4064      */
4065     /**
4066      * @event unload
4067      * 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.
4068      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4069      * @param {HtmlElement} t The target of the event.
4070      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4071      */
4072     /**
4073      * @event abort
4074      * Fires when an object/image is stopped from loading before completely loaded.
4075      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4076      * @param {HtmlElement} t The target of the event.
4077      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4078      */
4079     /**
4080      * @event error
4081      * Fires when an object/image/frame cannot be loaded properly.
4082      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4083      * @param {HtmlElement} t The target of the event.
4084      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4085      */
4086     /**
4087      * @event resize
4088      * Fires when a document view is resized.
4089      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4090      * @param {HtmlElement} t The target of the event.
4091      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4092      */
4093     /**
4094      * @event scroll
4095      * Fires when a document view is scrolled.
4096      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4097      * @param {HtmlElement} t The target of the event.
4098      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4099      */
4100
4101 //  Form events
4102     /**
4103      * @event select
4104      * Fires when a user selects some text in a text field, including input and textarea.
4105      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4106      * @param {HtmlElement} t The target of the event.
4107      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4108      */
4109     /**
4110      * @event change
4111      * Fires when a control loses the input focus and its value has been modified since gaining focus.
4112      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4113      * @param {HtmlElement} t The target of the event.
4114      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4115      */
4116     /**
4117      * @event submit
4118      * Fires when a form is submitted.
4119      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4120      * @param {HtmlElement} t The target of the event.
4121      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4122      */
4123     /**
4124      * @event reset
4125      * Fires when a form is reset.
4126      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4127      * @param {HtmlElement} t The target of the event.
4128      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4129      */
4130     /**
4131      * @event focus
4132      * Fires when an element receives focus either via the pointing device or by tab navigation.
4133      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4134      * @param {HtmlElement} t The target of the event.
4135      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4136      */
4137     /**
4138      * @event blur
4139      * Fires when an element loses focus either via the pointing device or by tabbing navigation.
4140      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4141      * @param {HtmlElement} t The target of the event.
4142      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4143      */
4144
4145 //  User Interface events
4146     /**
4147      * @event DOMFocusIn
4148      * Where supported. Similar to HTML focus event, but can be applied to any focusable element.
4149      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4150      * @param {HtmlElement} t The target of the event.
4151      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4152      */
4153     /**
4154      * @event DOMFocusOut
4155      * Where supported. Similar to HTML blur event, but can be applied to any focusable element.
4156      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4157      * @param {HtmlElement} t The target of the event.
4158      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4159      */
4160     /**
4161      * @event DOMActivate
4162      * Where supported. Fires when an element is activated, for instance, through a mouse click or a keypress.
4163      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4164      * @param {HtmlElement} t The target of the event.
4165      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4166      */
4167
4168 //  DOM Mutation events
4169     /**
4170      * @event DOMSubtreeModified
4171      * Where supported. Fires when the subtree is modified.
4172      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4173      * @param {HtmlElement} t The target of the event.
4174      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4175      */
4176     /**
4177      * @event DOMNodeInserted
4178      * Where supported. Fires when a node has been added as a child of another node.
4179      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4180      * @param {HtmlElement} t The target of the event.
4181      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4182      */
4183     /**
4184      * @event DOMNodeRemoved
4185      * Where supported. Fires when a descendant node of the element is removed.
4186      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4187      * @param {HtmlElement} t The target of the event.
4188      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4189      */
4190     /**
4191      * @event DOMNodeRemovedFromDocument
4192      * Where supported. Fires when a node is being removed from a document.
4193      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4194      * @param {HtmlElement} t The target of the event.
4195      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4196      */
4197     /**
4198      * @event DOMNodeInsertedIntoDocument
4199      * Where supported. Fires when a node is being inserted into a document.
4200      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4201      * @param {HtmlElement} t The target of the event.
4202      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4203      */
4204     /**
4205      * @event DOMAttrModified
4206      * Where supported. Fires when an attribute has been modified.
4207      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4208      * @param {HtmlElement} t The target of the event.
4209      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4210      */
4211     /**
4212      * @event DOMCharacterDataModified
4213      * Where supported. Fires when the character data has been modified.
4214      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4215      * @param {HtmlElement} t The target of the event.
4216      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4217      */
4218
4219     /**
4220      * The default unit to append to CSS values where a unit isn't provided (defaults to px).
4221      * @type String
4222      */
4223     defaultUnit : "px",
4224
4225     /**
4226      * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
4227      * @param {String} selector The simple selector to test
4228      * @return {Boolean} True if this element matches the selector, else false
4229      */
4230     is : function(simpleSelector){
4231         return Ext.DomQuery.is(this.dom, simpleSelector);
4232     },
4233
4234     /**
4235      * Tries to focus the element. Any exceptions are caught and ignored.
4236      * @param {Number} defer (optional) Milliseconds to defer the focus
4237      * @return {Ext.Element} this
4238      */
4239     focus : function(defer, /* private */ dom) {
4240         var me = this,
4241             dom = dom || me.dom;
4242         try{
4243             if(Number(defer)){
4244                 me.focus.defer(defer, null, [null, dom]);
4245             }else{
4246                 dom.focus();
4247             }
4248         }catch(e){}
4249         return me;
4250     },
4251
4252     /**
4253      * Tries to blur the element. Any exceptions are caught and ignored.
4254      * @return {Ext.Element} this
4255      */
4256     blur : function() {
4257         try{
4258             this.dom.blur();
4259         }catch(e){}
4260         return this;
4261     },
4262
4263     /**
4264      * Returns the value of the "value" attribute
4265      * @param {Boolean} asNumber true to parse the value as a number
4266      * @return {String/Number}
4267      */
4268     getValue : function(asNumber){
4269         var val = this.dom.value;
4270         return asNumber ? parseInt(val, 10) : val;
4271     },
4272
4273     /**
4274      * Appends an event handler to this element.  The shorthand version {@link #on} is equivalent.
4275      * @param {String} eventName The name of event to handle.
4276      * @param {Function} fn The handler function the event invokes. This function is passed
4277      * the following parameters:<ul>
4278      * <li><b>evt</b> : EventObject<div class="sub-desc">The {@link Ext.EventObject EventObject} describing the event.</div></li>
4279      * <li><b>el</b> : HtmlElement<div class="sub-desc">The DOM element which was the target of the event.
4280      * Note that this may be filtered by using the <tt>delegate</tt> option.</div></li>
4281      * <li><b>o</b> : Object<div class="sub-desc">The options object from the addListener call.</div></li>
4282      * </ul>
4283      * @param {Object} scope (optional) The scope (<code><b>this</b></code> reference) in which the handler function is executed.
4284      * <b>If omitted, defaults to this Element.</b>.
4285      * @param {Object} options (optional) An object containing handler configuration properties.
4286      * This may contain any of the following properties:<ul>
4287      * <li><b>scope</b> Object : <div class="sub-desc">The scope (<code><b>this</b></code> reference) in which the handler function is executed.
4288      * <b>If omitted, defaults to this Element.</b></div></li>
4289      * <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>
4290      * <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>
4291      * <li><b>preventDefault</b> Boolean: <div class="sub-desc">True to prevent the default action</div></li>
4292      * <li><b>stopPropagation</b> Boolean: <div class="sub-desc">True to prevent event propagation</div></li>
4293      * <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>
4294      * <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>
4295      * <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>
4296      * <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>
4297      * <li><b>buffer</b> Number: <div class="sub-desc">Causes the handler to be scheduled to run in an {@link Ext.util.DelayedTask} delayed
4298      * by the specified number of milliseconds. If the event fires again within that time, the original
4299      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</div></li>
4300      * </ul><br>
4301      * <p>
4302      * <b>Combining Options</b><br>
4303      * In the following examples, the shorthand form {@link #on} is used rather than the more verbose
4304      * addListener.  The two are equivalent.  Using the options argument, it is possible to combine different
4305      * types of listeners:<br>
4306      * <br>
4307      * A delayed, one-time listener that auto stops the event and adds a custom argument (forumId) to the
4308      * options object. The options object is available as the third parameter in the handler function.<div style="margin: 5px 20px 20px;">
4309      * Code:<pre><code>
4310 el.on('click', this.onClick, this, {
4311     single: true,
4312     delay: 100,
4313     stopEvent : true,
4314     forumId: 4
4315 });</code></pre></p>
4316      * <p>
4317      * <b>Attaching multiple handlers in 1 call</b><br>
4318      * The method also allows for a single argument to be passed which is a config object containing properties
4319      * which specify multiple handlers.</p>
4320      * <p>
4321      * Code:<pre><code>
4322 el.on({
4323     'click' : {
4324         fn: this.onClick,
4325         scope: this,
4326         delay: 100
4327     },
4328     'mouseover' : {
4329         fn: this.onMouseOver,
4330         scope: this
4331     },
4332     'mouseout' : {
4333         fn: this.onMouseOut,
4334         scope: this
4335     }
4336 });</code></pre>
4337      * <p>
4338      * Or a shorthand syntax:<br>
4339      * Code:<pre><code></p>
4340 el.on({
4341     'click' : this.onClick,
4342     'mouseover' : this.onMouseOver,
4343     'mouseout' : this.onMouseOut,
4344     scope: this
4345 });
4346      * </code></pre></p>
4347      * <p><b>delegate</b></p>
4348      * <p>This is a configuration option that you can pass along when registering a handler for
4349      * an event to assist with event delegation. Event delegation is a technique that is used to
4350      * reduce memory consumption and prevent exposure to memory-leaks. By registering an event
4351      * for a container element as opposed to each element within a container. By setting this
4352      * configuration option to a simple selector, the target element will be filtered to look for
4353      * a descendant of the target.
4354      * For example:<pre><code>
4355 // using this markup:
4356 &lt;div id='elId'>
4357     &lt;p id='p1'>paragraph one&lt;/p>
4358     &lt;p id='p2' class='clickable'>paragraph two&lt;/p>
4359     &lt;p id='p3'>paragraph three&lt;/p>
4360 &lt;/div>
4361 // utilize event delegation to registering just one handler on the container element:
4362 el = Ext.get('elId');
4363 el.on(
4364     'click',
4365     function(e,t) {
4366         // handle click
4367         console.info(t.id); // 'p2'
4368     },
4369     this,
4370     {
4371         // filter the target element to be a descendant with the class 'clickable'
4372         delegate: '.clickable'
4373     }
4374 );
4375      * </code></pre></p>
4376      * @return {Ext.Element} this
4377      */
4378     addListener : function(eventName, fn, scope, options){
4379         Ext.EventManager.on(this.dom,  eventName, fn, scope || this, options);
4380         return this;
4381     },
4382
4383     /**
4384      * Removes an event handler from this element.  The shorthand version {@link #un} is equivalent.
4385      * <b>Note</b>: if a <i>scope</i> was explicitly specified when {@link #addListener adding} the
4386      * listener, the same scope must be specified here.
4387      * Example:
4388      * <pre><code>
4389 el.removeListener('click', this.handlerFn);
4390 // or
4391 el.un('click', this.handlerFn);
4392 </code></pre>
4393      * @param {String} eventName The name of the event from which to remove the handler.
4394      * @param {Function} fn The handler function to remove. <b>This must be a reference to the function passed into the {@link #addListener} call.</b>
4395      * @param {Object} scope If a scope (<b><code>this</code></b> reference) was specified when the listener was added,
4396      * then this must refer to the same object.
4397      * @return {Ext.Element} this
4398      */
4399     removeListener : function(eventName, fn, scope){
4400         Ext.EventManager.removeListener(this.dom,  eventName, fn, scope || this);
4401         return this;
4402     },
4403
4404     /**
4405      * Removes all previous added listeners from this element
4406      * @return {Ext.Element} this
4407      */
4408     removeAllListeners : function(){
4409         Ext.EventManager.removeAll(this.dom);
4410         return this;
4411     },
4412
4413     /**
4414      * Recursively removes all previous added listeners from this element and its children
4415      * @return {Ext.Element} this
4416      */
4417     purgeAllListeners : function() {
4418         Ext.EventManager.purgeElement(this, true);
4419         return this;
4420     },
4421     /**
4422      * @private Test if size has a unit, otherwise appends the default
4423      */
4424     addUnits : function(size){
4425         if(size === "" || size == "auto" || size === undefined){
4426             size = size || '';
4427         } else if(!isNaN(size) || !unitPattern.test(size)){
4428             size = size + (this.defaultUnit || 'px');
4429         }
4430         return size;
4431     },
4432
4433     /**
4434      * <p>Updates the <a href="http://developer.mozilla.org/en/DOM/element.innerHTML">innerHTML</a> of this Element
4435      * 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>
4436      * <p>Updating innerHTML of an element will <b>not</b> execute embedded <tt>&lt;script></tt> elements. This is a browser restriction.</p>
4437      * @param {Mixed} options. Either a sring containing the URL from which to load the HTML, or an {@link Ext.Ajax#request} options object specifying
4438      * exactly how to request the HTML.
4439      * @return {Ext.Element} this
4440      */
4441     load : function(url, params, cb){
4442         Ext.Ajax.request(Ext.apply({
4443             params: params,
4444             url: url.url || url,
4445             callback: cb,
4446             el: this.dom,
4447             indicatorText: url.indicatorText || ''
4448         }, Ext.isObject(url) ? url : {}));
4449         return this;
4450     },
4451
4452     /**
4453      * Tests various css rules/browsers to determine if this element uses a border box
4454      * @return {Boolean}
4455      */
4456     isBorderBox : function(){
4457         return noBoxAdjust[(this.dom.tagName || "").toLowerCase()] || Ext.isBorderBox;
4458     },
4459
4460     /**
4461      * <p>Removes this element's dom reference.  Note that event and cache removal is handled at {@link Ext#removeNode}</p>
4462      */
4463     remove : function(){
4464         var me = this,
4465             dom = me.dom;
4466
4467         if (dom) {
4468             delete me.dom;
4469             Ext.removeNode(dom);
4470         }
4471     },
4472
4473     /**
4474      * Sets up event handlers to call the passed functions when the mouse is moved into and out of the Element.
4475      * @param {Function} overFn The function to call when the mouse enters the Element.
4476      * @param {Function} outFn The function to call when the mouse leaves the Element.
4477      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the functions are executed. Defaults to the Element's DOM element.
4478      * @param {Object} options (optional) Options for the listener. See {@link Ext.util.Observable#addListener the <tt>options</tt> parameter}.
4479      * @return {Ext.Element} this
4480      */
4481     hover : function(overFn, outFn, scope, options){
4482         var me = this;
4483         me.on('mouseenter', overFn, scope || me.dom, options);
4484         me.on('mouseleave', outFn, scope || me.dom, options);
4485         return me;
4486     },
4487
4488     /**
4489      * Returns true if this element is an ancestor of the passed element
4490      * @param {HTMLElement/String} el The element to check
4491      * @return {Boolean} True if this element is an ancestor of el, else false
4492      */
4493     contains : function(el){
4494         return !el ? false : Ext.lib.Dom.isAncestor(this.dom, el.dom ? el.dom : el);
4495     },
4496
4497     /**
4498      * Returns the value of a namespaced attribute from the element's underlying DOM node.
4499      * @param {String} namespace The namespace in which to look for the attribute
4500      * @param {String} name The attribute name
4501      * @return {String} The attribute value
4502      * @deprecated
4503      */
4504     getAttributeNS : function(ns, name){
4505         return this.getAttribute(name, ns);
4506     },
4507
4508     /**
4509      * Returns the value of an attribute from the element's underlying DOM node.
4510      * @param {String} name The attribute name
4511      * @param {String} namespace (optional) The namespace in which to look for the attribute
4512      * @return {String} The attribute value
4513      */
4514     getAttribute : Ext.isIE ? function(name, ns){
4515         var d = this.dom,
4516             type = typeof d[ns + ":" + name];
4517
4518         if(['undefined', 'unknown'].indexOf(type) == -1){
4519             return d[ns + ":" + name];
4520         }
4521         return d[name];
4522     } : function(name, ns){
4523         var d = this.dom;
4524         return d.getAttributeNS(ns, name) || d.getAttribute(ns + ":" + name) || d.getAttribute(name) || d[name];
4525     },
4526
4527     /**
4528     * Update the innerHTML of this element
4529     * @param {String} html The new HTML
4530     * @return {Ext.Element} this
4531      */
4532     update : function(html) {
4533         if (this.dom) {
4534             this.dom.innerHTML = html;
4535         }
4536         return this;
4537     }
4538 };
4539
4540 var ep = El.prototype;
4541
4542 El.addMethods = function(o){
4543    Ext.apply(ep, o);
4544 };
4545
4546 /**
4547  * Appends an event handler (shorthand for {@link #addListener}).
4548  * @param {String} eventName The name of event to handle.
4549  * @param {Function} fn The handler function the event invokes.
4550  * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the handler function is executed.
4551  * @param {Object} options (optional) An object containing standard {@link #addListener} options
4552  * @member Ext.Element
4553  * @method on
4554  */
4555 ep.on = ep.addListener;
4556
4557 /**
4558  * Removes an event handler from this element (see {@link #removeListener} for additional notes).
4559  * @param {String} eventName The name of the event from which to remove the handler.
4560  * @param {Function} fn The handler function to remove. <b>This must be a reference to the function passed into the {@link #addListener} call.</b>
4561  * @param {Object} scope If a scope (<b><code>this</code></b> reference) was specified when the listener was added,
4562  * then this must refer to the same object.
4563  * @return {Ext.Element} this
4564  * @member Ext.Element
4565  * @method un
4566  */
4567 ep.un = ep.removeListener;
4568
4569 /**
4570  * true to automatically adjust width and height settings for box-model issues (default to true)
4571  */
4572 ep.autoBoxAdjust = true;
4573
4574 // private
4575 var unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i,
4576     docEl;
4577
4578 /**
4579  * @private
4580  */
4581
4582 /**
4583  * Retrieves Ext.Element objects.
4584  * <p><b>This method does not retrieve {@link Ext.Component Component}s.</b> This method
4585  * retrieves Ext.Element objects which encapsulate DOM elements. To retrieve a Component by
4586  * its ID, use {@link Ext.ComponentMgr#get}.</p>
4587  * <p>Uses simple caching to consistently return the same object. Automatically fixes if an
4588  * object was recreated with the same id via AJAX or DOM.</p>
4589  * @param {Mixed} el The id of the node, a DOM Node or an existing Element.
4590  * @return {Element} The Element object (or null if no matching element was found)
4591  * @static
4592  * @member Ext.Element
4593  * @method get
4594  */
4595 El.get = function(el){
4596     var ex,
4597         elm,
4598         id;
4599     if(!el){ return null; }
4600     if (typeof el == "string") { // element id
4601         if (!(elm = DOC.getElementById(el))) {
4602             return null;
4603         }
4604         if (EC[el] && EC[el].el) {
4605             ex = EC[el].el;
4606             ex.dom = elm;
4607         } else {
4608             ex = El.addToCache(new El(elm));
4609         }
4610         return ex;
4611     } else if (el.tagName) { // dom element
4612         if(!(id = el.id)){
4613             id = Ext.id(el);
4614         }
4615         if (EC[id] && EC[id].el) {
4616             ex = EC[id].el;
4617             ex.dom = el;
4618         } else {
4619             ex = El.addToCache(new El(el));
4620         }
4621         return ex;
4622     } else if (el instanceof El) {
4623         if(el != docEl){
4624             // refresh dom element in case no longer valid,
4625             // catch case where it hasn't been appended
4626
4627             // If an el instance is passed, don't pass to getElementById without some kind of id
4628             if (Ext.isIE && (el.id == undefined || el.id == '')) {
4629                 el.dom = el.dom;
4630             } else {
4631                 el.dom = DOC.getElementById(el.id) || el.dom;
4632             }
4633         }
4634         return el;
4635     } else if(el.isComposite) {
4636         return el;
4637     } else if(Ext.isArray(el)) {
4638         return El.select(el);
4639     } else if(el == DOC) {
4640         // create a bogus element object representing the document object
4641         if(!docEl){
4642             var f = function(){};
4643             f.prototype = El.prototype;
4644             docEl = new f();
4645             docEl.dom = DOC;
4646         }
4647         return docEl;
4648     }
4649     return null;
4650 };
4651
4652 El.addToCache = function(el, id){
4653     id = id || el.id;
4654     EC[id] = {
4655         el:  el,
4656         data: {},
4657         events: {}
4658     };
4659     return el;
4660 };
4661
4662 // private method for getting and setting element data
4663 El.data = function(el, key, value){
4664     el = El.get(el);
4665     if (!el) {
4666         return null;
4667     }
4668     var c = EC[el.id].data;
4669     if(arguments.length == 2){
4670         return c[key];
4671     }else{
4672         return (c[key] = value);
4673     }
4674 };
4675
4676 // private
4677 // Garbage collection - uncache elements/purge listeners on orphaned elements
4678 // so we don't hold a reference and cause the browser to retain them
4679 function garbageCollect(){
4680     if(!Ext.enableGarbageCollector){
4681         clearInterval(El.collectorThreadId);
4682     } else {
4683         var eid,
4684             el,
4685             d,
4686             o;
4687
4688         for(eid in EC){
4689             o = EC[eid];
4690             if(o.skipGC){
4691                 continue;
4692             }
4693             el = o.el;
4694             d = el.dom;
4695             // -------------------------------------------------------
4696             // Determining what is garbage:
4697             // -------------------------------------------------------
4698             // !d
4699             // dom node is null, definitely garbage
4700             // -------------------------------------------------------
4701             // !d.parentNode
4702             // no parentNode == direct orphan, definitely garbage
4703             // -------------------------------------------------------
4704             // !d.offsetParent && !document.getElementById(eid)
4705             // display none elements have no offsetParent so we will
4706             // also try to look it up by it's id. However, check
4707             // offsetParent first so we don't do unneeded lookups.
4708             // This enables collection of elements that are not orphans
4709             // directly, but somewhere up the line they have an orphan
4710             // parent.
4711             // -------------------------------------------------------
4712             if(!d || !d.parentNode || (!d.offsetParent && !DOC.getElementById(eid))){
4713                 if(Ext.enableListenerCollection){
4714                     Ext.EventManager.removeAll(d);
4715                 }
4716                 delete EC[eid];
4717             }
4718         }
4719         // Cleanup IE Object leaks
4720         if (Ext.isIE) {
4721             var t = {};
4722             for (eid in EC) {
4723                 t[eid] = EC[eid];
4724             }
4725             EC = Ext.elCache = t;
4726         }
4727     }
4728 }
4729 El.collectorThreadId = setInterval(garbageCollect, 30000);
4730
4731 var flyFn = function(){};
4732 flyFn.prototype = El.prototype;
4733
4734 // dom is optional
4735 El.Flyweight = function(dom){
4736     this.dom = dom;
4737 };
4738
4739 El.Flyweight.prototype = new flyFn();
4740 El.Flyweight.prototype.isFlyweight = true;
4741 El._flyweights = {};
4742
4743 /**
4744  * <p>Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
4745  * the dom node can be overwritten by other code. Shorthand of {@link Ext.Element#fly}</p>
4746  * <p>Use this to make one-time references to DOM elements which are not going to be accessed again either by
4747  * application code, or by Ext's classes. If accessing an element which will be processed regularly, then {@link Ext#get}
4748  * will be more appropriate to take advantage of the caching provided by the Ext.Element class.</p>
4749  * @param {String/HTMLElement} el The dom node or id
4750  * @param {String} named (optional) Allows for creation of named reusable flyweights to prevent conflicts
4751  * (e.g. internally Ext uses "_global")
4752  * @return {Element} The shared Element object (or null if no matching element was found)
4753  * @member Ext.Element
4754  * @method fly
4755  */
4756 El.fly = function(el, named){
4757     var ret = null;
4758     named = named || '_global';
4759
4760     if (el = Ext.getDom(el)) {
4761         (El._flyweights[named] = El._flyweights[named] || new El.Flyweight()).dom = el;
4762         ret = El._flyweights[named];
4763     }
4764     return ret;
4765 };
4766
4767 /**
4768  * Retrieves Ext.Element objects.
4769  * <p><b>This method does not retrieve {@link Ext.Component Component}s.</b> This method
4770  * retrieves Ext.Element objects which encapsulate DOM elements. To retrieve a Component by
4771  * its ID, use {@link Ext.ComponentMgr#get}.</p>
4772  * <p>Uses simple caching to consistently return the same object. Automatically fixes if an
4773  * object was recreated with the same id via AJAX or DOM.</p>
4774  * Shorthand of {@link Ext.Element#get}
4775  * @param {Mixed} el The id of the node, a DOM Node or an existing Element.
4776  * @return {Element} The Element object (or null if no matching element was found)
4777  * @member Ext
4778  * @method get
4779  */
4780 Ext.get = El.get;
4781
4782 /**
4783  * <p>Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
4784  * the dom node can be overwritten by other code. Shorthand of {@link Ext.Element#fly}</p>
4785  * <p>Use this to make one-time references to DOM elements which are not going to be accessed again either by
4786  * application code, or by Ext's classes. If accessing an element which will be processed regularly, then {@link Ext#get}
4787  * will be more appropriate to take advantage of the caching provided by the Ext.Element class.</p>
4788  * @param {String/HTMLElement} el The dom node or id
4789  * @param {String} named (optional) Allows for creation of named reusable flyweights to prevent conflicts
4790  * (e.g. internally Ext uses "_global")
4791  * @return {Element} The shared Element object (or null if no matching element was found)
4792  * @member Ext
4793  * @method fly
4794  */
4795 Ext.fly = El.fly;
4796
4797 // speedy lookup for elements never to box adjust
4798 var noBoxAdjust = Ext.isStrict ? {
4799     select:1
4800 } : {
4801     input:1, select:1, textarea:1
4802 };
4803 if(Ext.isIE || Ext.isGecko){
4804     noBoxAdjust['button'] = 1;
4805 }
4806
4807 })();
4808 /**
4809  * @class Ext.Element
4810  */
4811 Ext.Element.addMethods({
4812     /**
4813      * Stops the specified event(s) from bubbling and optionally prevents the default action
4814      * @param {String/Array} eventName an event / array of events to stop from bubbling
4815      * @param {Boolean} preventDefault (optional) true to prevent the default action too
4816      * @return {Ext.Element} this
4817      */
4818     swallowEvent : function(eventName, preventDefault){
4819         var me = this;
4820         function fn(e){
4821             e.stopPropagation();
4822             if(preventDefault){
4823                 e.preventDefault();
4824             }
4825         }
4826         if(Ext.isArray(eventName)){
4827             Ext.each(eventName, function(e) {
4828                  me.on(e, fn);
4829             });
4830             return me;
4831         }
4832         me.on(eventName, fn);
4833         return me;
4834     },
4835
4836     /**
4837      * Create an event handler on this element such that when the event fires and is handled by this element,
4838      * it will be relayed to another object (i.e., fired again as if it originated from that object instead).
4839      * @param {String} eventName The type of event to relay
4840      * @param {Object} object Any object that extends {@link Ext.util.Observable} that will provide the context
4841      * for firing the relayed event
4842      */
4843     relayEvent : function(eventName, observable){
4844         this.on(eventName, function(e){
4845             observable.fireEvent(eventName, e);
4846         });
4847     },
4848
4849     /**
4850      * Removes worthless text nodes
4851      * @param {Boolean} forceReclean (optional) By default the element
4852      * keeps track if it has been cleaned already so
4853      * you can call this over and over. However, if you update the element and
4854      * need to force a reclean, you can pass true.
4855      */
4856     clean : function(forceReclean){
4857         var me = this,
4858             dom = me.dom,
4859             n = dom.firstChild,
4860             ni = -1;
4861
4862         if(Ext.Element.data(dom, 'isCleaned') && forceReclean !== true){
4863             return me;
4864         }
4865
4866         while(n){
4867             var nx = n.nextSibling;
4868             if(n.nodeType == 3 && !/\S/.test(n.nodeValue)){
4869                 dom.removeChild(n);
4870             }else{
4871                 n.nodeIndex = ++ni;
4872             }
4873             n = nx;
4874         }
4875         Ext.Element.data(dom, 'isCleaned', true);
4876         return me;
4877     },
4878
4879     /**
4880      * Direct access to the Updater {@link Ext.Updater#update} method. The method takes the same object
4881      * parameter as {@link Ext.Updater#update}
4882      * @return {Ext.Element} this
4883      */
4884     load : function(){
4885         var um = this.getUpdater();
4886         um.update.apply(um, arguments);
4887         return this;
4888     },
4889
4890     /**
4891     * Gets this element's {@link Ext.Updater Updater}
4892     * @return {Ext.Updater} The Updater
4893     */
4894     getUpdater : function(){
4895         return this.updateManager || (this.updateManager = new Ext.Updater(this));
4896     },
4897
4898     /**
4899     * Update the innerHTML of this element, optionally searching for and processing scripts
4900     * @param {String} html The new HTML
4901     * @param {Boolean} loadScripts (optional) True to look for and process scripts (defaults to false)
4902     * @param {Function} callback (optional) For async script loading you can be notified when the update completes
4903     * @return {Ext.Element} this
4904      */
4905     update : function(html, loadScripts, callback){
4906         if (!this.dom) {
4907             return this;
4908         }
4909         html = html || "";
4910
4911         if(loadScripts !== true){
4912             this.dom.innerHTML = html;
4913             if(Ext.isFunction(callback)){
4914                 callback();
4915             }
4916             return this;
4917         }
4918
4919         var id = Ext.id(),
4920             dom = this.dom;
4921
4922         html += '<span id="' + id + '"></span>';
4923
4924         Ext.lib.Event.onAvailable(id, function(){
4925             var DOC = document,
4926                 hd = DOC.getElementsByTagName("head")[0],
4927                 re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig,
4928                 srcRe = /\ssrc=([\'\"])(.*?)\1/i,
4929                 typeRe = /\stype=([\'\"])(.*?)\1/i,
4930                 match,
4931                 attrs,
4932                 srcMatch,
4933                 typeMatch,
4934                 el,
4935                 s;
4936
4937             while((match = re.exec(html))){
4938                 attrs = match[1];
4939                 srcMatch = attrs ? attrs.match(srcRe) : false;
4940                 if(srcMatch && srcMatch[2]){
4941                    s = DOC.createElement("script");
4942                    s.src = srcMatch[2];
4943                    typeMatch = attrs.match(typeRe);
4944                    if(typeMatch && typeMatch[2]){
4945                        s.type = typeMatch[2];
4946                    }
4947                    hd.appendChild(s);
4948                 }else if(match[2] && match[2].length > 0){
4949                     if(window.execScript) {
4950                        window.execScript(match[2]);
4951                     } else {
4952                        window.eval(match[2]);
4953                     }
4954                 }
4955             }
4956             el = DOC.getElementById(id);
4957             if(el){Ext.removeNode(el);}
4958             if(Ext.isFunction(callback)){
4959                 callback();
4960             }
4961         });
4962         dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
4963         return this;
4964     },
4965
4966     // inherit docs, overridden so we can add removeAnchor
4967     removeAllListeners : function(){
4968         this.removeAnchor();
4969         Ext.EventManager.removeAll(this.dom);
4970         return this;
4971     },
4972
4973     /**
4974      * Creates a proxy element of this element
4975      * @param {String/Object} config The class name of the proxy element or a DomHelper config object
4976      * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
4977      * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
4978      * @return {Ext.Element} The new proxy element
4979      */
4980     createProxy : function(config, renderTo, matchBox){
4981         config = Ext.isObject(config) ? config : {tag : "div", cls: config};
4982
4983         var me = this,
4984             proxy = renderTo ? Ext.DomHelper.append(renderTo, config, true) :
4985                                Ext.DomHelper.insertBefore(me.dom, config, true);
4986
4987         if(matchBox && me.setBox && me.getBox){ // check to make sure Element.position.js is loaded
4988            proxy.setBox(me.getBox());
4989         }
4990         return proxy;
4991     }
4992 });
4993
4994 Ext.Element.prototype.getUpdateManager = Ext.Element.prototype.getUpdater;
4995 /**
4996  * @class Ext.Element
4997  */
4998 Ext.Element.addMethods({
4999     /**
5000      * Gets the x,y coordinates specified by the anchor position on the element.
5001      * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo}
5002      * for details on supported anchor positions.
5003      * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead
5004      * of page coordinates
5005      * @param {Object} size (optional) An object containing the size to use for calculating anchor position
5006      * {width: (target width), height: (target height)} (defaults to the element's current size)
5007      * @return {Array} [x, y] An array containing the element's x and y coordinates
5008      */
5009     getAnchorXY : function(anchor, local, s){
5010         //Passing a different size is useful for pre-calculating anchors,
5011         //especially for anchored animations that change the el size.
5012                 anchor = (anchor || "tl").toLowerCase();
5013         s = s || {};
5014         
5015         var me = this,        
5016                 vp = me.dom == document.body || me.dom == document,
5017                 w = s.width || vp ? Ext.lib.Dom.getViewWidth() : me.getWidth(),
5018                 h = s.height || vp ? Ext.lib.Dom.getViewHeight() : me.getHeight(),                              
5019                 xy,             
5020                 r = Math.round,
5021                 o = me.getXY(),
5022                 scroll = me.getScroll(),
5023                 extraX = vp ? scroll.left : !local ? o[0] : 0,
5024                 extraY = vp ? scroll.top : !local ? o[1] : 0,
5025                 hash = {
5026                         c  : [r(w * 0.5), r(h * 0.5)],
5027                         t  : [r(w * 0.5), 0],
5028                         l  : [0, r(h * 0.5)],
5029                         r  : [w, r(h * 0.5)],
5030                         b  : [r(w * 0.5), h],
5031                         tl : [0, 0],    
5032                         bl : [0, h],
5033                         br : [w, h],
5034                         tr : [w, 0]
5035                 };
5036         
5037         xy = hash[anchor];      
5038         return [xy[0] + extraX, xy[1] + extraY]; 
5039     },
5040
5041     /**
5042      * Anchors an element to another element and realigns it when the window is resized.
5043      * @param {Mixed} element The element to align to.
5044      * @param {String} position The position to align to.
5045      * @param {Array} offsets (optional) Offset the positioning by [x, y]
5046      * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
5047      * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
5048      * is a number, it is used as the buffer delay (defaults to 50ms).
5049      * @param {Function} callback The function to call after the animation finishes
5050      * @return {Ext.Element} this
5051      */
5052     anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){        
5053             var me = this,
5054             dom = me.dom,
5055             scroll = !Ext.isEmpty(monitorScroll),
5056             action = function(){
5057                 Ext.fly(dom).alignTo(el, alignment, offsets, animate);
5058                 Ext.callback(callback, Ext.fly(dom));
5059             },
5060             anchor = this.getAnchor();
5061             
5062         // previous listener anchor, remove it
5063         this.removeAnchor();
5064         Ext.apply(anchor, {
5065             fn: action,
5066             scroll: scroll
5067         });
5068
5069         Ext.EventManager.onWindowResize(action, null);
5070         
5071         if(scroll){
5072             Ext.EventManager.on(window, 'scroll', action, null,
5073                 {buffer: !isNaN(monitorScroll) ? monitorScroll : 50});
5074         }
5075         action.call(me); // align immediately
5076         return me;
5077     },
5078     
5079     /**
5080      * Remove any anchor to this element. See {@link #anchorTo}.
5081      * @return {Ext.Element} this
5082      */
5083     removeAnchor : function(){
5084         var me = this,
5085             anchor = this.getAnchor();
5086             
5087         if(anchor && anchor.fn){
5088             Ext.EventManager.removeResizeListener(anchor.fn);
5089             if(anchor.scroll){
5090                 Ext.EventManager.un(window, 'scroll', anchor.fn);
5091             }
5092             delete anchor.fn;
5093         }
5094         return me;
5095     },
5096     
5097     // private
5098     getAnchor : function(){
5099         var data = Ext.Element.data,
5100             dom = this.dom;
5101             if (!dom) {
5102                 return;
5103             }
5104             var anchor = data(dom, '_anchor');
5105             
5106         if(!anchor){
5107             anchor = data(dom, '_anchor', {});
5108         }
5109         return anchor;
5110     },
5111
5112     /**
5113      * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
5114      * supported position values.
5115      * @param {Mixed} element The element to align to.
5116      * @param {String} position (optional, defaults to "tl-bl?") The position to align to.
5117      * @param {Array} offsets (optional) Offset the positioning by [x, y]
5118      * @return {Array} [x, y]
5119      */
5120     getAlignToXY : function(el, p, o){      
5121         el = Ext.get(el);
5122         
5123         if(!el || !el.dom){
5124             throw "Element.alignToXY with an element that doesn't exist";
5125         }
5126         
5127         o = o || [0,0];
5128         p = (!p || p == "?" ? "tl-bl?" : (!/-/.test(p) && p !== "" ? "tl-" + p : p || "tl-bl")).toLowerCase();       
5129                 
5130         var me = this,
5131                 d = me.dom,
5132                 a1,
5133                 a2,
5134                 x,
5135                 y,
5136                 //constrain the aligned el to viewport if necessary
5137                 w,
5138                 h,
5139                 r,
5140                 dw = Ext.lib.Dom.getViewWidth() -10, // 10px of margin for ie
5141                 dh = Ext.lib.Dom.getViewHeight()-10, // 10px of margin for ie
5142                 p1y,
5143                 p1x,            
5144                 p2y,
5145                 p2x,
5146                 swapY,
5147                 swapX,
5148                 doc = document,
5149                 docElement = doc.documentElement,
5150                 docBody = doc.body,
5151                 scrollX = (docElement.scrollLeft || docBody.scrollLeft || 0)+5,
5152                 scrollY = (docElement.scrollTop || docBody.scrollTop || 0)+5,
5153                 c = false, //constrain to viewport
5154                 p1 = "", 
5155                 p2 = "",
5156                 m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
5157         
5158         if(!m){
5159            throw "Element.alignTo with an invalid alignment " + p;
5160         }
5161         
5162         p1 = m[1]; 
5163         p2 = m[2]; 
5164         c = !!m[3];
5165
5166         //Subtract the aligned el's internal xy from the target's offset xy
5167         //plus custom offset to get the aligned el's new offset xy
5168         a1 = me.getAnchorXY(p1, true);
5169         a2 = el.getAnchorXY(p2, false);
5170
5171         x = a2[0] - a1[0] + o[0];
5172         y = a2[1] - a1[1] + o[1];
5173
5174         if(c){    
5175                w = me.getWidth();
5176            h = me.getHeight();
5177            r = el.getRegion();       
5178            //If we are at a viewport boundary and the aligned el is anchored on a target border that is
5179            //perpendicular to the vp border, allow the aligned el to slide on that border,
5180            //otherwise swap the aligned el to the opposite border of the target.
5181            p1y = p1.charAt(0);
5182            p1x = p1.charAt(p1.length-1);
5183            p2y = p2.charAt(0);
5184            p2x = p2.charAt(p2.length-1);
5185            swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
5186            swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));          
5187            
5188
5189            if (x + w > dw + scrollX) {
5190                 x = swapX ? r.left-w : dw+scrollX-w;
5191            }
5192            if (x < scrollX) {
5193                x = swapX ? r.right : scrollX;
5194            }
5195            if (y + h > dh + scrollY) {
5196                 y = swapY ? r.top-h : dh+scrollY-h;
5197             }
5198            if (y < scrollY){
5199                y = swapY ? r.bottom : scrollY;
5200            }
5201         }
5202         return [x,y];
5203     },
5204
5205     /**
5206      * Aligns this element with another element relative to the specified anchor points. If the other element is the
5207      * document it aligns it to the viewport.
5208      * The position parameter is optional, and can be specified in any one of the following formats:
5209      * <ul>
5210      *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
5211      *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
5212      *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
5213      *       deprecated in favor of the newer two anchor syntax below</i>.</li>
5214      *   <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
5215      *       element's anchor point, and the second value is used as the target's anchor point.</li>
5216      * </ul>
5217      * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
5218      * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
5219      * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
5220      * that specified in order to enforce the viewport constraints.
5221      * Following are all of the supported anchor positions:
5222 <pre>
5223 Value  Description
5224 -----  -----------------------------
5225 tl     The top left corner (default)
5226 t      The center of the top edge
5227 tr     The top right corner
5228 l      The center of the left edge
5229 c      In the center of the element
5230 r      The center of the right edge
5231 bl     The bottom left corner
5232 b      The center of the bottom edge
5233 br     The bottom right corner
5234 </pre>
5235 Example Usage:
5236 <pre><code>
5237 // align el to other-el using the default positioning ("tl-bl", non-constrained)
5238 el.alignTo("other-el");
5239
5240 // align the top left corner of el with the top right corner of other-el (constrained to viewport)
5241 el.alignTo("other-el", "tr?");
5242
5243 // align the bottom right corner of el with the center left edge of other-el
5244 el.alignTo("other-el", "br-l?");
5245
5246 // align the center of el with the bottom left corner of other-el and
5247 // adjust the x position by -6 pixels (and the y position by 0)
5248 el.alignTo("other-el", "c-bl", [-6, 0]);
5249 </code></pre>
5250      * @param {Mixed} element The element to align to.
5251      * @param {String} position (optional, defaults to "tl-bl?") The position to align to.
5252      * @param {Array} offsets (optional) Offset the positioning by [x, y]
5253      * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
5254      * @return {Ext.Element} this
5255      */
5256     alignTo : function(element, position, offsets, animate){
5257             var me = this;
5258         return me.setXY(me.getAlignToXY(element, position, offsets),
5259                                 me.preanim && !!animate ? me.preanim(arguments, 3) : false);
5260     },
5261     
5262     // private ==>  used outside of core
5263     adjustForConstraints : function(xy, parent, offsets){
5264         return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
5265     },
5266
5267     // private ==>  used outside of core
5268     getConstrainToXY : function(el, local, offsets, proposedXY){   
5269             var os = {top:0, left:0, bottom:0, right: 0};
5270
5271         return function(el, local, offsets, proposedXY){
5272             el = Ext.get(el);
5273             offsets = offsets ? Ext.applyIf(offsets, os) : os;
5274
5275             var vw, vh, vx = 0, vy = 0;
5276             if(el.dom == document.body || el.dom == document){
5277                 vw =Ext.lib.Dom.getViewWidth();
5278                 vh = Ext.lib.Dom.getViewHeight();
5279             }else{
5280                 vw = el.dom.clientWidth;
5281                 vh = el.dom.clientHeight;
5282                 if(!local){
5283                     var vxy = el.getXY();
5284                     vx = vxy[0];
5285                     vy = vxy[1];
5286                 }
5287             }
5288
5289             var s = el.getScroll();
5290
5291             vx += offsets.left + s.left;
5292             vy += offsets.top + s.top;
5293
5294             vw -= offsets.right;
5295             vh -= offsets.bottom;
5296
5297             var vr = vx+vw;
5298             var vb = vy+vh;
5299
5300             var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
5301             var x = xy[0], y = xy[1];
5302             var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
5303
5304             // only move it if it needs it
5305             var moved = false;
5306
5307             // first validate right/bottom
5308             if((x + w) > vr){
5309                 x = vr - w;
5310                 moved = true;
5311             }
5312             if((y + h) > vb){
5313                 y = vb - h;
5314                 moved = true;
5315             }
5316             // then make sure top/left isn't negative
5317             if(x < vx){
5318                 x = vx;
5319                 moved = true;
5320             }
5321             if(y < vy){
5322                 y = vy;
5323                 moved = true;
5324             }
5325             return moved ? [x, y] : false;
5326         };
5327     }(),
5328             
5329             
5330                 
5331 //         el = Ext.get(el);
5332 //         offsets = Ext.applyIf(offsets || {}, {top : 0, left : 0, bottom : 0, right : 0});
5333
5334 //         var  me = this,
5335 //              doc = document,
5336 //              s = el.getScroll(),
5337 //              vxy = el.getXY(),
5338 //              vx = offsets.left + s.left, 
5339 //              vy = offsets.top + s.top,               
5340 //              vw = -offsets.right, 
5341 //              vh = -offsets.bottom, 
5342 //              vr,
5343 //              vb,
5344 //              xy = proposedXY || (!local ? me.getXY() : [me.getLeft(true), me.getTop(true)]),
5345 //              x = xy[0],
5346 //              y = xy[1],
5347 //              w = me.dom.offsetWidth, h = me.dom.offsetHeight,
5348 //              moved = false; // only move it if it needs it
5349 //       
5350 //              
5351 //         if(el.dom == doc.body || el.dom == doc){
5352 //             vw += Ext.lib.Dom.getViewWidth();
5353 //             vh += Ext.lib.Dom.getViewHeight();
5354 //         }else{
5355 //             vw += el.dom.clientWidth;
5356 //             vh += el.dom.clientHeight;
5357 //             if(!local){                    
5358 //                 vx += vxy[0];
5359 //                 vy += vxy[1];
5360 //             }
5361 //         }
5362
5363 //         // first validate right/bottom
5364 //         if(x + w > vx + vw){
5365 //             x = vx + vw - w;
5366 //             moved = true;
5367 //         }
5368 //         if(y + h > vy + vh){
5369 //             y = vy + vh - h;
5370 //             moved = true;
5371 //         }
5372 //         // then make sure top/left isn't negative
5373 //         if(x < vx){
5374 //             x = vx;
5375 //             moved = true;
5376 //         }
5377 //         if(y < vy){
5378 //             y = vy;
5379 //             moved = true;
5380 //         }
5381 //         return moved ? [x, y] : false;
5382 //    },
5383     
5384     /**
5385     * Calculates the x, y to center this element on the screen
5386     * @return {Array} The x, y values [x, y]
5387     */
5388     getCenterXY : function(){
5389         return this.getAlignToXY(document, 'c-c');
5390     },
5391
5392     /**
5393     * Centers the Element in either the viewport, or another Element.
5394     * @param {Mixed} centerIn (optional) The element in which to center the element.
5395     */
5396     center : function(centerIn){
5397         return this.alignTo(centerIn || document, 'c-c');        
5398     }    
5399 });
5400 /**
5401  * @class Ext.Element
5402  */
5403 Ext.Element.addMethods(function(){
5404         var PARENTNODE = 'parentNode',
5405                 NEXTSIBLING = 'nextSibling',
5406                 PREVIOUSSIBLING = 'previousSibling',
5407                 DQ = Ext.DomQuery,
5408                 GET = Ext.get;
5409         
5410         return {
5411                 /**
5412              * 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)
5413              * @param {String} selector The simple selector to test
5414              * @param {Number/Mixed} maxDepth (optional) The max depth to search as a number or element (defaults to 50 || document.body)
5415              * @param {Boolean} returnEl (optional) True to return a Ext.Element object instead of DOM node
5416              * @return {HTMLElement} The matching DOM node (or null if no match was found)
5417              */
5418             findParent : function(simpleSelector, maxDepth, returnEl){
5419                 var p = this.dom,
5420                         b = document.body, 
5421                         depth = 0,                      
5422                         stopEl;         
5423             if(Ext.isGecko && Object.prototype.toString.call(p) == '[object XULElement]') {
5424                 return null;
5425             }
5426                 maxDepth = maxDepth || 50;
5427                 if (isNaN(maxDepth)) {
5428                     stopEl = Ext.getDom(maxDepth);
5429                     maxDepth = Number.MAX_VALUE;
5430                 }
5431                 while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
5432                     if(DQ.is(p, simpleSelector)){
5433                         return returnEl ? GET(p) : p;
5434                     }
5435                     depth++;
5436                     p = p.parentNode;
5437                 }
5438                 return null;
5439             },
5440         
5441             /**
5442              * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
5443              * @param {String} selector The simple selector to test
5444              * @param {Number/Mixed} maxDepth (optional) The max depth to
5445                     search as a number or element (defaults to 10 || document.body)
5446              * @param {Boolean} returnEl (optional) True to return a Ext.Element object instead of DOM node
5447              * @return {HTMLElement} The matching DOM node (or null if no match was found)
5448              */
5449             findParentNode : function(simpleSelector, maxDepth, returnEl){
5450                 var p = Ext.fly(this.dom.parentNode, '_internal');
5451                 return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
5452             },
5453         
5454             /**
5455              * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
5456              * This is a shortcut for findParentNode() that always returns an Ext.Element.
5457              * @param {String} selector The simple selector to test
5458              * @param {Number/Mixed} maxDepth (optional) The max depth to
5459                     search as a number or element (defaults to 10 || document.body)
5460              * @return {Ext.Element} The matching DOM node (or null if no match was found)
5461              */
5462             up : function(simpleSelector, maxDepth){
5463                 return this.findParentNode(simpleSelector, maxDepth, true);
5464             },
5465         
5466             /**
5467              * Creates a {@link Ext.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
5468              * @param {String} selector The CSS selector
5469              * @return {CompositeElement/CompositeElementLite} The composite element
5470              */
5471             select : function(selector){
5472                 return Ext.Element.select(selector, this.dom);
5473             },
5474         
5475             /**
5476              * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
5477              * @param {String} selector The CSS selector
5478              * @return {Array} An array of the matched nodes
5479              */
5480             query : function(selector){
5481                 return DQ.select(selector, this.dom);
5482             },
5483         
5484             /**
5485              * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
5486              * @param {String} selector The CSS selector
5487              * @param {Boolean} returnDom (optional) True to return the DOM node instead of Ext.Element (defaults to false)
5488              * @return {HTMLElement/Ext.Element} The child Ext.Element (or DOM node if returnDom = true)
5489              */
5490             child : function(selector, returnDom){
5491                 var n = DQ.selectNode(selector, this.dom);
5492                 return returnDom ? n : GET(n);
5493             },
5494         
5495             /**
5496              * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
5497              * @param {String} selector The CSS selector
5498              * @param {Boolean} returnDom (optional) True to return the DOM node instead of Ext.Element (defaults to false)
5499              * @return {HTMLElement/Ext.Element} The child Ext.Element (or DOM node if returnDom = true)
5500              */
5501             down : function(selector, returnDom){
5502                 var n = DQ.selectNode(" > " + selector, this.dom);
5503                 return returnDom ? n : GET(n);
5504             },
5505         
5506                  /**
5507              * Gets the parent node for this element, optionally chaining up trying to match a selector
5508              * @param {String} selector (optional) Find a parent node that matches the passed simple selector
5509              * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.Element
5510              * @return {Ext.Element/HTMLElement} The parent node or null
5511                  */
5512             parent : function(selector, returnDom){
5513                 return this.matchNode(PARENTNODE, PARENTNODE, selector, returnDom);
5514             },
5515         
5516              /**
5517              * Gets the next sibling, skipping text nodes
5518              * @param {String} selector (optional) Find the next sibling that matches the passed simple selector
5519              * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.Element
5520              * @return {Ext.Element/HTMLElement} The next sibling or null
5521                  */
5522             next : function(selector, returnDom){
5523                 return this.matchNode(NEXTSIBLING, NEXTSIBLING, selector, returnDom);
5524             },
5525         
5526             /**
5527              * Gets the previous sibling, skipping text nodes
5528              * @param {String} selector (optional) Find the previous sibling that matches the passed simple selector
5529              * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.Element
5530              * @return {Ext.Element/HTMLElement} The previous sibling or null
5531                  */
5532             prev : function(selector, returnDom){
5533                 return this.matchNode(PREVIOUSSIBLING, PREVIOUSSIBLING, selector, returnDom);
5534             },
5535         
5536         
5537             /**
5538              * Gets the first child, skipping text nodes
5539              * @param {String} selector (optional) Find the next sibling that matches the passed simple selector
5540              * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.Element
5541              * @return {Ext.Element/HTMLElement} The first child or null
5542                  */
5543             first : function(selector, returnDom){
5544                 return this.matchNode(NEXTSIBLING, 'firstChild', selector, returnDom);
5545             },
5546         
5547             /**
5548              * Gets the last child, skipping text nodes
5549              * @param {String} selector (optional) Find the previous sibling that matches the passed simple selector
5550              * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.Element
5551              * @return {Ext.Element/HTMLElement} The last child or null
5552                  */
5553             last : function(selector, returnDom){
5554                 return this.matchNode(PREVIOUSSIBLING, 'lastChild', selector, returnDom);
5555             },
5556             
5557             matchNode : function(dir, start, selector, returnDom){
5558                 var n = this.dom[start];
5559                 while(n){
5560                     if(n.nodeType == 1 && (!selector || DQ.is(n, selector))){
5561                         return !returnDom ? GET(n) : n;
5562                     }
5563                     n = n[dir];
5564                 }
5565                 return null;
5566             }   
5567     }
5568 }());/**
5569  * @class Ext.Element
5570  */
5571 Ext.Element.addMethods({
5572     /**
5573      * Creates a {@link Ext.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
5574      * @param {String} selector The CSS selector
5575      * @param {Boolean} unique (optional) True to create a unique Ext.Element for each child (defaults to false, which creates a single shared flyweight object)
5576      * @return {CompositeElement/CompositeElementLite} The composite element
5577      */
5578     select : function(selector, unique){
5579         return Ext.Element.select(selector, unique, this.dom);
5580     }
5581 });/**
5582  * @class Ext.Element
5583  */
5584 Ext.Element.addMethods(
5585 function() {
5586         var GETDOM = Ext.getDom,
5587                 GET = Ext.get,
5588                 DH = Ext.DomHelper;
5589         
5590         return {
5591             /**
5592              * Appends the passed element(s) to this element
5593              * @param {String/HTMLElement/Array/Element/CompositeElement} el
5594              * @return {Ext.Element} this
5595              */
5596             appendChild: function(el){        
5597                 return GET(el).appendTo(this);        
5598             },
5599         
5600             /**
5601              * Appends this element to the passed element
5602              * @param {Mixed} el The new parent element
5603              * @return {Ext.Element} this
5604              */
5605             appendTo: function(el){        
5606                 GETDOM(el).appendChild(this.dom);        
5607                 return this;
5608             },
5609         
5610             /**
5611              * Inserts this element before the passed element in the DOM
5612              * @param {Mixed} el The element before which this element will be inserted
5613              * @return {Ext.Element} this
5614              */
5615             insertBefore: function(el){                   
5616                 (el = GETDOM(el)).parentNode.insertBefore(this.dom, el);
5617                 return this;
5618             },
5619         
5620             /**
5621              * Inserts this element after the passed element in the DOM
5622              * @param {Mixed} el The element to insert after
5623              * @return {Ext.Element} this
5624              */
5625             insertAfter: function(el){
5626                 (el = GETDOM(el)).parentNode.insertBefore(this.dom, el.nextSibling);
5627                 return this;
5628             },
5629         
5630             /**
5631              * Inserts (or creates) an element (or DomHelper config) as the first child of this element
5632              * @param {Mixed/Object} el The id or element to insert or a DomHelper config to create and insert
5633              * @return {Ext.Element} The new child
5634              */
5635             insertFirst: function(el, returnDom){
5636             el = el || {};
5637             if(el.nodeType || el.dom || typeof el == 'string'){ // element
5638                 el = GETDOM(el);
5639                 this.dom.insertBefore(el, this.dom.firstChild);
5640                 return !returnDom ? GET(el) : el;
5641             }else{ // dh config
5642                 return this.createChild(el, this.dom.firstChild, returnDom);
5643             }
5644         },
5645         
5646             /**
5647              * Replaces the passed element with this element
5648              * @param {Mixed} el The element to replace
5649              * @return {Ext.Element} this
5650              */
5651             replace: function(el){
5652                 el = GET(el);
5653                 this.insertBefore(el);
5654                 el.remove();
5655                 return this;
5656             },
5657         
5658             /**
5659              * Replaces this element with the passed element
5660              * @param {Mixed/Object} el The new element or a DomHelper config of an element to create
5661              * @return {Ext.Element} this
5662              */
5663             replaceWith: function(el){
5664                     var me = this;
5665                 
5666             if(el.nodeType || el.dom || typeof el == 'string'){
5667                 el = GETDOM(el);
5668                 me.dom.parentNode.insertBefore(el, me.dom);
5669             }else{
5670                 el = DH.insertBefore(me.dom, el);
5671             }
5672                 
5673                 delete Ext.elCache[me.id];
5674                 Ext.removeNode(me.dom);      
5675                 me.id = Ext.id(me.dom = el);
5676                 Ext.Element.addToCache(me.isFlyweight ? new Ext.Element(me.dom) : me);     
5677             return me;
5678             },
5679             
5680                 /**
5681                  * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
5682                  * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
5683                  * automatically generated with the specified attributes.
5684                  * @param {HTMLElement} insertBefore (optional) a child element of this element
5685                  * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
5686                  * @return {Ext.Element} The new child element
5687                  */
5688                 createChild: function(config, insertBefore, returnDom){
5689                     config = config || {tag:'div'};
5690                     return insertBefore ? 
5691                            DH.insertBefore(insertBefore, config, returnDom !== true) :  
5692                            DH[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
5693                 },
5694                 
5695                 /**
5696                  * Creates and wraps this element with another element
5697                  * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
5698                  * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Ext.Element
5699                  * @return {HTMLElement/Element} The newly created wrapper element
5700                  */
5701                 wrap: function(config, returnDom){        
5702                     var newEl = DH.insertBefore(this.dom, config || {tag: "div"}, !returnDom);
5703                     newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
5704                     return newEl;
5705                 },
5706                 
5707                 /**
5708                  * Inserts an html fragment into this element
5709                  * @param {String} where Where to insert the html in relation to this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
5710                  * @param {String} html The HTML fragment
5711                  * @param {Boolean} returnEl (optional) True to return an Ext.Element (defaults to false)
5712                  * @return {HTMLElement/Ext.Element} The inserted node (or nearest related if more than 1 inserted)
5713                  */
5714                 insertHtml : function(where, html, returnEl){
5715                     var el = DH.insertHtml(where, this.dom, html);
5716                     return returnEl ? Ext.get(el) : el;
5717                 }
5718         }
5719 }());/**
5720  * @class Ext.Element
5721  */
5722 Ext.apply(Ext.Element.prototype, function() {
5723         var GETDOM = Ext.getDom,
5724                 GET = Ext.get,
5725                 DH = Ext.DomHelper;
5726         
5727         return {        
5728                 /**
5729              * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
5730              * @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.
5731              * @param {String} where (optional) 'before' or 'after' defaults to before
5732              * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Ext.Element
5733              * @return {Ext.Element} The inserted Element. If an array is passed, the last inserted element is returned.
5734              */
5735             insertSibling: function(el, where, returnDom){
5736                 var me = this,
5737                         rt,
5738                 isAfter = (where || 'before').toLowerCase() == 'after',
5739                 insertEl;
5740                         
5741                 if(Ext.isArray(el)){
5742                 insertEl = me;
5743                     Ext.each(el, function(e) {
5744                             rt = Ext.fly(insertEl, '_internal').insertSibling(e, where, returnDom);
5745                     if(isAfter){
5746                         insertEl = rt;
5747                     }
5748                     });
5749                     return rt;
5750                 }
5751                         
5752                 el = el || {};
5753                 
5754             if(el.nodeType || el.dom){
5755                 rt = me.dom.parentNode.insertBefore(GETDOM(el), isAfter ? me.dom.nextSibling : me.dom);
5756                 if (!returnDom) {
5757                     rt = GET(rt);
5758                 }
5759             }else{
5760                 if (isAfter && !me.dom.nextSibling) {
5761                     rt = DH.append(me.dom.parentNode, el, !returnDom);
5762                 } else {                    
5763                     rt = DH[isAfter ? 'insertAfter' : 'insertBefore'](me.dom, el, !returnDom);
5764                 }
5765             }
5766                 return rt;
5767             }
5768     };
5769 }());/**
5770  * @class Ext.Element
5771  */
5772 Ext.Element.addMethods(function(){
5773     // local style camelizing for speed
5774     var propCache = {},
5775         camelRe = /(-[a-z])/gi,
5776         classReCache = {},
5777         view = document.defaultView,
5778         propFloat = Ext.isIE ? 'styleFloat' : 'cssFloat',
5779         opacityRe = /alpha\(opacity=(.*)\)/i,
5780         trimRe = /^\s+|\s+$/g,
5781         EL = Ext.Element,
5782         PADDING = "padding",
5783         MARGIN = "margin",
5784         BORDER = "border",
5785         LEFT = "-left",
5786         RIGHT = "-right",
5787         TOP = "-top",
5788         BOTTOM = "-bottom",
5789         WIDTH = "-width",
5790         MATH = Math,
5791         HIDDEN = 'hidden',
5792         ISCLIPPED = 'isClipped',
5793         OVERFLOW = 'overflow',
5794         OVERFLOWX = 'overflow-x',
5795         OVERFLOWY = 'overflow-y',
5796         ORIGINALCLIP = 'originalClip',
5797         // special markup used throughout Ext when box wrapping elements
5798         borders = {l: BORDER + LEFT + WIDTH, r: BORDER + RIGHT + WIDTH, t: BORDER + TOP + WIDTH, b: BORDER + BOTTOM + WIDTH},
5799         paddings = {l: PADDING + LEFT, r: PADDING + RIGHT, t: PADDING + TOP, b: PADDING + BOTTOM},
5800         margins = {l: MARGIN + LEFT, r: MARGIN + RIGHT, t: MARGIN + TOP, b: MARGIN + BOTTOM},
5801         data = Ext.Element.data;
5802
5803
5804     // private
5805     function camelFn(m, a) {
5806         return a.charAt(1).toUpperCase();
5807     }
5808
5809     function chkCache(prop) {
5810         return propCache[prop] || (propCache[prop] = prop == 'float' ? propFloat : prop.replace(camelRe, camelFn));
5811     }
5812
5813     return {
5814         // private  ==> used by Fx
5815         adjustWidth : function(width) {
5816             var me = this;
5817             var isNum = Ext.isNumber(width);
5818             if(isNum && me.autoBoxAdjust && !me.isBorderBox()){
5819                width -= (me.getBorderWidth("lr") + me.getPadding("lr"));
5820             }
5821             return (isNum && width < 0) ? 0 : width;
5822         },
5823
5824         // private   ==> used by Fx
5825         adjustHeight : function(height) {
5826             var me = this;
5827             var isNum = Ext.isNumber(height);
5828             if(isNum && me.autoBoxAdjust && !me.isBorderBox()){
5829                height -= (me.getBorderWidth("tb") + me.getPadding("tb"));
5830             }
5831             return (isNum && height < 0) ? 0 : height;
5832         },
5833
5834
5835         /**
5836          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
5837          * @param {String/Array} className The CSS class to add, or an array of classes
5838          * @return {Ext.Element} this
5839          */
5840         addClass : function(className){
5841             var me = this, i, len, v;
5842             className = Ext.isArray(className) ? className : [className];
5843             for (i=0, len = className.length; i < len; i++) {
5844                 v = className[i];
5845                 if (v) {
5846                     me.dom.className += (!me.hasClass(v) && v ? " " + v : "");
5847                 };
5848             };
5849             return me;
5850         },
5851
5852         /**
5853          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
5854          * @param {String/Array} className The CSS class to add, or an array of classes
5855          * @return {Ext.Element} this
5856          */
5857         radioClass : function(className){
5858             var cn = this.dom.parentNode.childNodes, v;
5859             className = Ext.isArray(className) ? className : [className];
5860             for (var i=0, len = cn.length; i < len; i++) {
5861                 v = cn[i];
5862                 if(v && v.nodeType == 1) {
5863                     Ext.fly(v, '_internal').removeClass(className);
5864                 }
5865             };
5866             return this.addClass(className);
5867         },
5868
5869         /**
5870          * Removes one or more CSS classes from the element.
5871          * @param {String/Array} className The CSS class to remove, or an array of classes
5872          * @return {Ext.Element} this
5873          */
5874         removeClass : function(className){
5875             var me = this, v;
5876             className = Ext.isArray(className) ? className : [className];
5877             if (me.dom && me.dom.className) {
5878                 for (var i=0, len=className.length; i < len; i++) {
5879                     v = className[i];
5880                     if(v) {
5881                         me.dom.className = me.dom.className.replace(
5882                             classReCache[v] = classReCache[v] || new RegExp('(?:^|\\s+)' + v + '(?:\\s+|$)', "g"), " "
5883                         );
5884                     }
5885                 };
5886             }
5887             return me;
5888         },
5889
5890         /**
5891          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
5892          * @param {String} className The CSS class to toggle
5893          * @return {Ext.Element} this
5894          */
5895         toggleClass : function(className){
5896             return this.hasClass(className) ? this.removeClass(className) : this.addClass(className);
5897         },
5898
5899         /**
5900          * Checks if the specified CSS class exists on this element's DOM node.
5901          * @param {String} className The CSS class to check for
5902          * @return {Boolean} True if the class exists, else false
5903          */
5904         hasClass : function(className){
5905             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
5906         },
5907
5908         /**
5909          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
5910          * @param {String} oldClassName The CSS class to replace
5911          * @param {String} newClassName The replacement CSS class
5912          * @return {Ext.Element} this
5913          */
5914         replaceClass : function(oldClassName, newClassName){
5915             return this.removeClass(oldClassName).addClass(newClassName);
5916         },
5917
5918         isStyle : function(style, val) {
5919             return this.getStyle(style) == val;
5920         },
5921
5922         /**
5923          * Normalizes currentStyle and computedStyle.
5924          * @param {String} property The style property whose value is returned.
5925          * @return {String} The current value of the style property for this element.
5926          */
5927         getStyle : function(){
5928             return view && view.getComputedStyle ?
5929                 function(prop){
5930                     var el = this.dom,
5931                         v,
5932                         cs,
5933                         out,
5934                         display,
5935                         wk = Ext.isWebKit,
5936                         display;
5937                         
5938                     if(el == document){
5939                         return null;
5940                     }
5941                     prop = chkCache(prop);
5942                     // Fix bug caused by this: https://bugs.webkit.org/show_bug.cgi?id=13343
5943                     if(wk && /marginRight/.test(prop)){
5944                         display = this.getStyle('display');
5945                         el.style.display = 'inline-block';
5946                     }
5947                     out = (v = el.style[prop]) ? v :
5948                            (cs = view.getComputedStyle(el, "")) ? cs[prop] : null;
5949
5950                     // Webkit returns rgb values for transparent.
5951                     if(wk){
5952                         if(out == 'rgba(0, 0, 0, 0)'){
5953                             out = 'transparent';
5954                         }else if(display){
5955                             el.style.display = display;
5956                         }
5957                     }
5958                     return out;
5959                 } :
5960                 function(prop){
5961                     var el = this.dom,
5962                         m,
5963                         cs;
5964
5965                     if(el == document) return null;
5966                     if (prop == 'opacity') {
5967                         if (el.style.filter.match) {
5968                             if(m = el.style.filter.match(opacityRe)){
5969                                 var fv = parseFloat(m[1]);
5970                                 if(!isNaN(fv)){
5971                                     return fv ? fv / 100 : 0;
5972                                 }
5973                             }
5974                         }
5975                         return 1;
5976                     }
5977                     prop = chkCache(prop);
5978                     return el.style[prop] || ((cs = el.currentStyle) ? cs[prop] : null);
5979                 };
5980         }(),
5981
5982         /**
5983          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
5984          * are convert to standard 6 digit hex color.
5985          * @param {String} attr The css attribute
5986          * @param {String} defaultValue The default value to use when a valid color isn't found
5987          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
5988          * color anims.
5989          */
5990         getColor : function(attr, defaultValue, prefix){
5991             var v = this.getStyle(attr),
5992                 color = Ext.isDefined(prefix) ? prefix : '#',
5993                 h;
5994
5995             if(!v || /transparent|inherit/.test(v)){
5996                 return defaultValue;
5997             }
5998             if(/^r/.test(v)){
5999                 Ext.each(v.slice(4, v.length -1).split(','), function(s){
6000                     h = parseInt(s, 10);
6001                     color += (h < 16 ? '0' : '') + h.toString(16);
6002                 });
6003             }else{
6004                 v = v.replace('#', '');
6005                 color += v.length == 3 ? v.replace(/^(\w)(\w)(\w)$/, '$1$1$2$2$3$3') : v;
6006             }
6007             return(color.length > 5 ? color.toLowerCase() : defaultValue);
6008         },
6009
6010         /**
6011          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
6012          * @param {String/Object} property The style property to be set, or an object of multiple styles.
6013          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
6014          * @return {Ext.Element} this
6015          */
6016         setStyle : function(prop, value){
6017             var tmp,
6018                 style,
6019                 camel;
6020             if (!Ext.isObject(prop)) {
6021                 tmp = {};
6022                 tmp[prop] = value;
6023                 prop = tmp;
6024             }
6025             for (style in prop) {
6026                 value = prop[style];
6027                 style == 'opacity' ?
6028                     this.setOpacity(value) :
6029                     this.dom.style[chkCache(style)] = value;
6030             }
6031             return this;
6032         },
6033
6034         /**
6035          * Set the opacity of the element
6036          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
6037          * @param {Boolean/Object} animate (optional) a standard Element animation config object or <tt>true</tt> for
6038          * the default animation (<tt>{duration: .35, easing: 'easeIn'}</tt>)
6039          * @return {Ext.Element} this
6040          */
6041          setOpacity : function(opacity, animate){
6042             var me = this,
6043                 s = me.dom.style;
6044
6045             if(!animate || !me.anim){
6046                 if(Ext.isIE){
6047                     var opac = opacity < 1 ? 'alpha(opacity=' + opacity * 100 + ')' : '',
6048                     val = s.filter.replace(opacityRe, '').replace(trimRe, '');
6049
6050                     s.zoom = 1;
6051                     s.filter = val + (val.length > 0 ? ' ' : '') + opac;
6052                 }else{
6053                     s.opacity = opacity;
6054                 }
6055             }else{
6056                 me.anim({opacity: {to: opacity}}, me.preanim(arguments, 1), null, .35, 'easeIn');
6057             }
6058             return me;
6059         },
6060
6061         /**
6062          * Clears any opacity settings from this element. Required in some cases for IE.
6063          * @return {Ext.Element} this
6064          */
6065         clearOpacity : function(){
6066             var style = this.dom.style;
6067             if(Ext.isIE){
6068                 if(!Ext.isEmpty(style.filter)){
6069                     style.filter = style.filter.replace(opacityRe, '').replace(trimRe, '');
6070                 }
6071             }else{
6072                 style.opacity = style['-moz-opacity'] = style['-khtml-opacity'] = '';
6073             }
6074             return this;
6075         },
6076
6077         /**
6078          * Returns the offset height of the element
6079          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
6080          * @return {Number} The element's height
6081          */
6082         getHeight : function(contentHeight){
6083             var me = this,
6084                 dom = me.dom,
6085                 hidden = Ext.isIE && me.isStyle('display', 'none'),
6086                 h = MATH.max(dom.offsetHeight, hidden ? 0 : dom.clientHeight) || 0;
6087
6088             h = !contentHeight ? h : h - me.getBorderWidth("tb") - me.getPadding("tb");
6089             return h < 0 ? 0 : h;
6090         },
6091
6092         /**
6093          * Returns the offset width of the element
6094          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
6095          * @return {Number} The element's width
6096          */
6097         getWidth : function(contentWidth){
6098             var me = this,
6099                 dom = me.dom,
6100                 hidden = Ext.isIE && me.isStyle('display', 'none'),
6101                 w = MATH.max(dom.offsetWidth, hidden ? 0 : dom.clientWidth) || 0;
6102             w = !contentWidth ? w : w - me.getBorderWidth("lr") - me.getPadding("lr");
6103             return w < 0 ? 0 : w;
6104         },
6105
6106         /**
6107          * Set the width of this Element.
6108          * @param {Mixed} width The new width. This may be one of:<div class="mdetail-params"><ul>
6109          * <li>A Number specifying the new width in this Element's {@link #defaultUnit}s (by default, pixels).</li>
6110          * <li>A String used to set the CSS width style. Animation may <b>not</b> be used.
6111          * </ul></div>
6112          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
6113          * @return {Ext.Element} this
6114          */
6115         setWidth : function(width, animate){
6116             var me = this;
6117             width = me.adjustWidth(width);
6118             !animate || !me.anim ?
6119                 me.dom.style.width = me.addUnits(width) :
6120                 me.anim({width : {to : width}}, me.preanim(arguments, 1));
6121             return me;
6122         },
6123
6124         /**
6125          * Set the height of this Element.
6126          * <pre><code>
6127 // change the height to 200px and animate with default configuration
6128 Ext.fly('elementId').setHeight(200, true);
6129
6130 // change the height to 150px and animate with a custom configuration
6131 Ext.fly('elId').setHeight(150, {
6132     duration : .5, // animation will have a duration of .5 seconds
6133     // will change the content to "finished"
6134     callback: function(){ this.{@link #update}("finished"); }
6135 });
6136          * </code></pre>
6137          * @param {Mixed} height The new height. This may be one of:<div class="mdetail-params"><ul>
6138          * <li>A Number specifying the new height in this Element's {@link #defaultUnit}s (by default, pixels.)</li>
6139          * <li>A String used to set the CSS height style. Animation may <b>not</b> be used.</li>
6140          * </ul></div>
6141          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
6142          * @return {Ext.Element} this
6143          */
6144          setHeight : function(height, animate){
6145             var me = this;
6146             height = me.adjustHeight(height);
6147             !animate || !me.anim ?
6148                 me.dom.style.height = me.addUnits(height) :
6149                 me.anim({height : {to : height}}, me.preanim(arguments, 1));
6150             return me;
6151         },
6152
6153         /**
6154          * Gets the width of the border(s) for the specified side(s)
6155          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
6156          * passing <tt>'lr'</tt> would get the border <b><u>l</u></b>eft width + the border <b><u>r</u></b>ight width.
6157          * @return {Number} The width of the sides passed added together
6158          */
6159         getBorderWidth : function(side){
6160             return this.addStyles(side, borders);
6161         },
6162
6163         /**
6164          * Gets the width of the padding(s) for the specified side(s)
6165          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
6166          * passing <tt>'lr'</tt> would get the padding <b><u>l</u></b>eft + the padding <b><u>r</u></b>ight.
6167          * @return {Number} The padding of the sides passed added together
6168          */
6169         getPadding : function(side){
6170             return this.addStyles(side, paddings);
6171         },
6172
6173         /**
6174          *  Store the current overflow setting and clip overflow on the element - use <tt>{@link #unclip}</tt> to remove
6175          * @return {Ext.Element} this
6176          */
6177         clip : function(){
6178             var me = this,
6179                 dom = me.dom;
6180
6181             if(!data(dom, ISCLIPPED)){
6182                 data(dom, ISCLIPPED, true);
6183                 data(dom, ORIGINALCLIP, {
6184                     o: me.getStyle(OVERFLOW),
6185                     x: me.getStyle(OVERFLOWX),
6186                     y: me.getStyle(OVERFLOWY)
6187                 });
6188                 me.setStyle(OVERFLOW, HIDDEN);
6189                 me.setStyle(OVERFLOWX, HIDDEN);
6190                 me.setStyle(OVERFLOWY, HIDDEN);
6191             }
6192             return me;
6193         },
6194
6195         /**
6196          *  Return clipping (overflow) to original clipping before <tt>{@link #clip}</tt> was called
6197          * @return {Ext.Element} this
6198          */
6199         unclip : function(){
6200             var me = this,
6201                 dom = me.dom;
6202
6203             if(data(dom, ISCLIPPED)){
6204                 data(dom, ISCLIPPED, false);
6205                 var o = data(dom, ORIGINALCLIP);
6206                 if(o.o){
6207                     me.setStyle(OVERFLOW, o.o);
6208                 }
6209                 if(o.x){
6210                     me.setStyle(OVERFLOWX, o.x);
6211                 }
6212                 if(o.y){
6213                     me.setStyle(OVERFLOWY, o.y);
6214                 }
6215             }
6216             return me;
6217         },
6218
6219         // private
6220         addStyles : function(sides, styles){
6221             var val = 0,
6222                 m = sides.match(/\w/g),
6223                 s;
6224             for (var i=0, len=m.length; i<len; i++) {
6225                 s = m[i] && parseInt(this.getStyle(styles[m[i]]), 10);
6226                 if (s) {
6227                     val += MATH.abs(s);
6228                 }
6229             }
6230             return val;
6231         },
6232
6233         margins : margins
6234     }
6235 }()
6236 );
6237 /**
6238  * @class Ext.Element
6239  */
6240
6241 // special markup used throughout Ext when box wrapping elements
6242 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>';
6243
6244 Ext.Element.addMethods(function(){
6245     var INTERNAL = "_internal",
6246         pxMatch = /(\d+\.?\d+)px/;
6247     return {
6248         /**
6249          * More flexible version of {@link #setStyle} for setting style properties.
6250          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
6251          * a function which returns such a specification.
6252          * @return {Ext.Element} this
6253          */
6254         applyStyles : function(style){
6255             Ext.DomHelper.applyStyles(this.dom, style);
6256             return this;
6257         },
6258
6259         /**
6260          * Returns an object with properties matching the styles requested.
6261          * For example, el.getStyles('color', 'font-size', 'width') might return
6262          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
6263          * @param {String} style1 A style name
6264          * @param {String} style2 A style name
6265          * @param {String} etc.
6266          * @return {Object} The style object
6267          */
6268         getStyles : function(){
6269             var ret = {};
6270             Ext.each(arguments, function(v) {
6271                ret[v] = this.getStyle(v);
6272             },
6273             this);
6274             return ret;
6275         },
6276
6277         // private  ==> used by ext full
6278         setOverflow : function(v){
6279             var dom = this.dom;
6280             if(v=='auto' && Ext.isMac && Ext.isGecko2){ // work around stupid FF 2.0/Mac scroll bar bug
6281                 dom.style.overflow = 'hidden';
6282                 (function(){dom.style.overflow = 'auto';}).defer(1);
6283             }else{
6284                 dom.style.overflow = v;
6285             }
6286         },
6287
6288        /**
6289         * <p>Wraps the specified element with a special 9 element markup/CSS block that renders by default as
6290         * a gray container with a gradient background, rounded corners and a 4-way shadow.</p>
6291         * <p>This special markup is used throughout Ext when box wrapping elements ({@link Ext.Button},
6292         * {@link Ext.Panel} when <tt>{@link Ext.Panel#frame frame=true}</tt>, {@link Ext.Window}).  The markup
6293         * is of this form:</p>
6294         * <pre><code>
6295     Ext.Element.boxMarkup =
6296     &#39;&lt;div class="{0}-tl">&lt;div class="{0}-tr">&lt;div class="{0}-tc">&lt;/div>&lt;/div>&lt;/div>
6297      &lt;div class="{0}-ml">&lt;div class="{0}-mr">&lt;div class="{0}-mc">&lt;/div>&lt;/div>&lt;/div>
6298      &lt;div class="{0}-bl">&lt;div class="{0}-br">&lt;div class="{0}-bc">&lt;/div>&lt;/div>&lt;/div>&#39;;
6299         * </code></pre>
6300         * <p>Example usage:</p>
6301         * <pre><code>
6302     // Basic box wrap
6303     Ext.get("foo").boxWrap();
6304
6305     // You can also add a custom class and use CSS inheritance rules to customize the box look.
6306     // 'x-box-blue' is a built-in alternative -- look at the related CSS definitions as an example
6307     // for how to create a custom box wrap style.
6308     Ext.get("foo").boxWrap().addClass("x-box-blue");
6309         * </code></pre>
6310         * @param {String} class (optional) A base CSS class to apply to the containing wrapper element
6311         * (defaults to <tt>'x-box'</tt>). Note that there are a number of CSS rules that are dependent on
6312         * this name to make the overall effect work, so if you supply an alternate base class, make sure you
6313         * also supply all of the necessary rules.
6314         * @return {Ext.Element} The outermost wrapping element of the created box structure.
6315         */
6316         boxWrap : function(cls){
6317             cls = cls || 'x-box';
6318             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)));
6319             Ext.DomQuery.selectNode('.' + cls + '-mc', el.dom).appendChild(this.dom);
6320             return el;
6321         },
6322
6323         /**
6324          * Set the size of this Element. If animation is true, both width and height will be animated concurrently.
6325          * @param {Mixed} width The new width. This may be one of:<div class="mdetail-params"><ul>
6326          * <li>A Number specifying the new width in this Element's {@link #defaultUnit}s (by default, pixels).</li>
6327          * <li>A String used to set the CSS width style. Animation may <b>not</b> be used.
6328          * <li>A size object in the format <code>{width: widthValue, height: heightValue}</code>.</li>
6329          * </ul></div>
6330          * @param {Mixed} height The new height. This may be one of:<div class="mdetail-params"><ul>
6331          * <li>A Number specifying the new height in this Element's {@link #defaultUnit}s (by default, pixels).</li>
6332          * <li>A String used to set the CSS height style. Animation may <b>not</b> be used.</li>
6333          * </ul></div>
6334          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
6335          * @return {Ext.Element} this
6336          */
6337         setSize : function(width, height, animate){
6338             var me = this;
6339             if(Ext.isObject(width)){ // in case of object from getSize()
6340                 height = width.height;
6341                 width = width.width;
6342             }
6343             width = me.adjustWidth(width);
6344             height = me.adjustHeight(height);
6345             if(!animate || !me.anim){
6346                 me.dom.style.width = me.addUnits(width);
6347                 me.dom.style.height = me.addUnits(height);
6348             }else{
6349                 me.anim({width: {to: width}, height: {to: height}}, me.preanim(arguments, 2));
6350             }
6351             return me;
6352         },
6353
6354         /**
6355          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
6356          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
6357          * if a height has not been set using CSS.
6358          * @return {Number}
6359          */
6360         getComputedHeight : function(){
6361             var me = this,
6362                 h = Math.max(me.dom.offsetHeight, me.dom.clientHeight);
6363             if(!h){
6364                 h = parseFloat(me.getStyle('height')) || 0;
6365                 if(!me.isBorderBox()){
6366                     h += me.getFrameWidth('tb');
6367                 }
6368             }
6369             return h;
6370         },
6371
6372         /**
6373          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
6374          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
6375          * if a width has not been set using CSS.
6376          * @return {Number}
6377          */
6378         getComputedWidth : function(){
6379             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
6380             if(!w){
6381                 w = parseFloat(this.getStyle('width')) || 0;
6382                 if(!this.isBorderBox()){
6383                     w += this.getFrameWidth('lr');
6384                 }
6385             }
6386             return w;
6387         },
6388
6389         /**
6390          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
6391          for more information about the sides.
6392          * @param {String} sides
6393          * @return {Number}
6394          */
6395         getFrameWidth : function(sides, onlyContentBox){
6396             return onlyContentBox && this.isBorderBox() ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
6397         },
6398
6399         /**
6400          * Sets up event handlers to add and remove a css class when the mouse is over this element
6401          * @param {String} className
6402          * @return {Ext.Element} this
6403          */
6404         addClassOnOver : function(className){
6405             this.hover(
6406                 function(){
6407                     Ext.fly(this, INTERNAL).addClass(className);
6408                 },
6409                 function(){
6410                     Ext.fly(this, INTERNAL).removeClass(className);
6411                 }
6412             );
6413             return this;
6414         },
6415
6416         /**
6417          * Sets up event handlers to add and remove a css class when this element has the focus
6418          * @param {String} className
6419          * @return {Ext.Element} this
6420          */
6421         addClassOnFocus : function(className){
6422             this.on("focus", function(){
6423                 Ext.fly(this, INTERNAL).addClass(className);
6424             }, this.dom);
6425             this.on("blur", function(){
6426                 Ext.fly(this, INTERNAL).removeClass(className);
6427             }, this.dom);
6428             return this;
6429         },
6430
6431         /**
6432          * 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)
6433          * @param {String} className
6434          * @return {Ext.Element} this
6435          */
6436         addClassOnClick : function(className){
6437             var dom = this.dom;
6438             this.on("mousedown", function(){
6439                 Ext.fly(dom, INTERNAL).addClass(className);
6440                 var d = Ext.getDoc(),
6441                     fn = function(){
6442                         Ext.fly(dom, INTERNAL).removeClass(className);
6443                         d.removeListener("mouseup", fn);
6444                     };
6445                 d.on("mouseup", fn);
6446             });
6447             return this;
6448         },
6449
6450         /**
6451          * <p>Returns the dimensions of the element available to lay content out in.<p>
6452          * <p>If the element (or any ancestor element) has CSS style <code>display : none</code>, the dimensions will be zero.</p>
6453          * example:<pre><code>
6454         var vpSize = Ext.getBody().getViewSize();
6455
6456         // all Windows created afterwards will have a default value of 90% height and 95% width
6457         Ext.Window.override({
6458             width: vpSize.width * 0.9,
6459             height: vpSize.height * 0.95
6460         });
6461         // To handle window resizing you would have to hook onto onWindowResize.
6462         * </code></pre>
6463         *
6464         * getViewSize utilizes clientHeight/clientWidth which excludes sizing of scrollbars.
6465         * To obtain the size including scrollbars, use getStyleSize
6466         *
6467         * Sizing of the document body is handled at the adapter level which handles special cases for IE and strict modes, etc.
6468         */
6469
6470         getViewSize : function(){
6471             var doc = document,
6472                 d = this.dom,
6473                 isDoc = (d == doc || d == doc.body);
6474
6475             // If the body, use Ext.lib.Dom
6476             if (isDoc) {
6477                 var extdom = Ext.lib.Dom;
6478                 return {
6479                     width : extdom.getViewWidth(),
6480                     height : extdom.getViewHeight()
6481                 };
6482
6483             // Else use clientHeight/clientWidth
6484             } else {
6485                 return {
6486                     width : d.clientWidth,
6487                     height : d.clientHeight
6488                 }
6489             }
6490         },
6491
6492         /**
6493         * <p>Returns the dimensions of the element available to lay content out in.<p>
6494         *
6495         * getStyleSize utilizes prefers style sizing if present, otherwise it chooses the larger of offsetHeight/clientHeight and offsetWidth/clientWidth.
6496         * To obtain the size excluding scrollbars, use getViewSize
6497         *
6498         * Sizing of the document body is handled at the adapter level which handles special cases for IE and strict modes, etc.
6499         */
6500
6501         getStyleSize : function(){
6502             var me = this,
6503                 w, h,
6504                 doc = document,
6505                 d = this.dom,
6506                 isDoc = (d == doc || d == doc.body),
6507                 s = d.style;
6508
6509             // If the body, use Ext.lib.Dom
6510             if (isDoc) {
6511                 var extdom = Ext.lib.Dom;
6512                 return {
6513                     width : extdom.getViewWidth(),
6514                     height : extdom.getViewHeight()
6515                 }
6516             }
6517             // Use Styles if they are set
6518             if(s.width && s.width != 'auto'){
6519                 w = parseFloat(s.width);
6520                 if(me.isBorderBox()){
6521                    w -= me.getFrameWidth('lr');
6522                 }
6523             }
6524             // Use Styles if they are set
6525             if(s.height && s.height != 'auto'){
6526                 h = parseFloat(s.height);
6527                 if(me.isBorderBox()){
6528                    h -= me.getFrameWidth('tb');
6529                 }
6530             }
6531             // Use getWidth/getHeight if style not set.
6532             return {width: w || me.getWidth(true), height: h || me.getHeight(true)};
6533         },
6534
6535         /**
6536          * Returns the size of the element.
6537          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
6538          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
6539          */
6540         getSize : function(contentSize){
6541             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
6542         },
6543
6544         /**
6545          * Forces the browser to repaint this element
6546          * @return {Ext.Element} this
6547          */
6548         repaint : function(){
6549             var dom = this.dom;
6550             this.addClass("x-repaint");
6551             setTimeout(function(){
6552                 Ext.fly(dom).removeClass("x-repaint");
6553             }, 1);
6554             return this;
6555         },
6556
6557         /**
6558          * Disables text selection for this element (normalized across browsers)
6559          * @return {Ext.Element} this
6560          */
6561         unselectable : function(){
6562             this.dom.unselectable = "on";
6563             return this.swallowEvent("selectstart", true).
6564                         applyStyles("-moz-user-select:none;-khtml-user-select:none;").
6565                         addClass("x-unselectable");
6566         },
6567
6568         /**
6569          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
6570          * then it returns the calculated width of the sides (see getPadding)
6571          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
6572          * @return {Object/Number}
6573          */
6574         getMargins : function(side){
6575             var me = this,
6576                 key,
6577                 hash = {t:"top", l:"left", r:"right", b: "bottom"},
6578                 o = {};
6579
6580             if (!side) {
6581                 for (key in me.margins){
6582                     o[hash[key]] = parseFloat(me.getStyle(me.margins[key])) || 0;
6583                 }
6584                 return o;
6585             } else {
6586                 return me.addStyles.call(me, side, me.margins);
6587             }
6588         }
6589     };
6590 }());
6591 /**
6592  * @class Ext.Element
6593  */
6594 (function(){
6595 var D = Ext.lib.Dom,
6596         LEFT = "left",
6597         RIGHT = "right",
6598         TOP = "top",
6599         BOTTOM = "bottom",
6600         POSITION = "position",
6601         STATIC = "static",
6602         RELATIVE = "relative",
6603         AUTO = "auto",
6604         ZINDEX = "z-index";
6605
6606 Ext.Element.addMethods({
6607         /**
6608       * 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).
6609       * @return {Number} The X position of the element
6610       */
6611     getX : function(){
6612         return D.getX(this.dom);
6613     },
6614
6615     /**
6616       * 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).
6617       * @return {Number} The Y position of the element
6618       */
6619     getY : function(){
6620         return D.getY(this.dom);
6621     },
6622
6623     /**
6624       * 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).
6625       * @return {Array} The XY position of the element
6626       */
6627     getXY : function(){
6628         return D.getXY(this.dom);
6629     },
6630
6631     /**
6632       * 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.
6633       * @param {Mixed} element The element to get the offsets from.
6634       * @return {Array} The XY page offsets (e.g. [100, -200])
6635       */
6636     getOffsetsTo : function(el){
6637         var o = this.getXY(),
6638                 e = Ext.fly(el, '_internal').getXY();
6639         return [o[0]-e[0],o[1]-e[1]];
6640     },
6641
6642     /**
6643      * 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).
6644      * @param {Number} The X position of the element
6645      * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
6646      * @return {Ext.Element} this
6647      */
6648     setX : function(x, animate){            
6649             return this.setXY([x, this.getY()], this.animTest(arguments, animate, 1));
6650     },
6651
6652     /**
6653      * 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).
6654      * @param {Number} The Y position of the element
6655      * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
6656      * @return {Ext.Element} this
6657      */
6658     setY : function(y, animate){            
6659             return this.setXY([this.getX(), y], this.animTest(arguments, animate, 1));
6660     },
6661
6662     /**
6663      * Sets the element's left position directly using CSS style (instead of {@link #setX}).
6664      * @param {String} left The left CSS property value
6665      * @return {Ext.Element} this
6666      */
6667     setLeft : function(left){
6668         this.setStyle(LEFT, this.addUnits(left));
6669         return this;
6670     },
6671
6672     /**
6673      * Sets the element's top position directly using CSS style (instead of {@link #setY}).
6674      * @param {String} top The top CSS property value
6675      * @return {Ext.Element} this
6676      */
6677     setTop : function(top){
6678         this.setStyle(TOP, this.addUnits(top));
6679         return this;
6680     },
6681
6682     /**
6683      * Sets the element's CSS right style.
6684      * @param {String} right The right CSS property value
6685      * @return {Ext.Element} this
6686      */
6687     setRight : function(right){
6688         this.setStyle(RIGHT, this.addUnits(right));
6689         return this;
6690     },
6691
6692     /**
6693      * Sets the element's CSS bottom style.
6694      * @param {String} bottom The bottom CSS property value
6695      * @return {Ext.Element} this
6696      */
6697     setBottom : function(bottom){
6698         this.setStyle(BOTTOM, this.addUnits(bottom));
6699         return this;
6700     },
6701
6702     /**
6703      * Sets the position of the element in page coordinates, regardless of how the element is positioned.
6704      * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
6705      * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
6706      * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
6707      * @return {Ext.Element} this
6708      */
6709     setXY : function(pos, animate){
6710             var me = this;
6711         if(!animate || !me.anim){
6712             D.setXY(me.dom, pos);
6713         }else{
6714             me.anim({points: {to: pos}}, me.preanim(arguments, 1), 'motion');
6715         }
6716         return me;
6717     },
6718
6719     /**
6720      * Sets the position of the element in page coordinates, regardless of how the element is positioned.
6721      * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
6722      * @param {Number} x X value for new position (coordinates are page-based)
6723      * @param {Number} y Y value for new position (coordinates are page-based)
6724      * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
6725      * @return {Ext.Element} this
6726      */
6727     setLocation : function(x, y, animate){
6728         return this.setXY([x, y], this.animTest(arguments, animate, 2));
6729     },
6730
6731     /**
6732      * Sets the position of the element in page coordinates, regardless of how the element is positioned.
6733      * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
6734      * @param {Number} x X value for new position (coordinates are page-based)
6735      * @param {Number} y Y value for new position (coordinates are page-based)
6736      * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
6737      * @return {Ext.Element} this
6738      */
6739     moveTo : function(x, y, animate){
6740         return this.setXY([x, y], this.animTest(arguments, animate, 2));        
6741     },    
6742     
6743     /**
6744      * Gets the left X coordinate
6745      * @param {Boolean} local True to get the local css position instead of page coordinate
6746      * @return {Number}
6747      */
6748     getLeft : function(local){
6749             return !local ? this.getX() : parseInt(this.getStyle(LEFT), 10) || 0;
6750     },
6751
6752     /**
6753      * Gets the right X coordinate of the element (element X position + element width)
6754      * @param {Boolean} local True to get the local css position instead of page coordinate
6755      * @return {Number}
6756      */
6757     getRight : function(local){
6758             var me = this;
6759             return !local ? me.getX() + me.getWidth() : (me.getLeft(true) + me.getWidth()) || 0;
6760     },
6761
6762     /**
6763      * Gets the top Y coordinate
6764      * @param {Boolean} local True to get the local css position instead of page coordinate
6765      * @return {Number}
6766      */
6767     getTop : function(local) {
6768             return !local ? this.getY() : parseInt(this.getStyle(TOP), 10) || 0;
6769     },
6770
6771     /**
6772      * Gets the bottom Y coordinate of the element (element Y position + element height)
6773      * @param {Boolean} local True to get the local css position instead of page coordinate
6774      * @return {Number}
6775      */
6776     getBottom : function(local){
6777             var me = this;
6778             return !local ? me.getY() + me.getHeight() : (me.getTop(true) + me.getHeight()) || 0;
6779     },
6780
6781     /**
6782     * Initializes positioning on this element. If a desired position is not passed, it will make the
6783     * the element positioned relative IF it is not already positioned.
6784     * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
6785     * @param {Number} zIndex (optional) The zIndex to apply
6786     * @param {Number} x (optional) Set the page X position
6787     * @param {Number} y (optional) Set the page Y position
6788     */
6789     position : function(pos, zIndex, x, y){
6790             var me = this;
6791             
6792         if(!pos && me.isStyle(POSITION, STATIC)){           
6793             me.setStyle(POSITION, RELATIVE);           
6794         } else if(pos) {
6795             me.setStyle(POSITION, pos);
6796         }
6797         if(zIndex){
6798             me.setStyle(ZINDEX, zIndex);
6799         }
6800         if(x || y) me.setXY([x || false, y || false]);
6801     },
6802
6803     /**
6804     * Clear positioning back to the default when the document was loaded
6805     * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
6806     * @return {Ext.Element} this
6807      */
6808     clearPositioning : function(value){
6809         value = value || '';
6810         this.setStyle({
6811             left : value,
6812             right : value,
6813             top : value,
6814             bottom : value,
6815             "z-index" : "",
6816             position : STATIC
6817         });
6818         return this;
6819     },
6820
6821     /**
6822     * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
6823     * snapshot before performing an update and then restoring the element.
6824     * @return {Object}
6825     */
6826     getPositioning : function(){
6827         var l = this.getStyle(LEFT);
6828         var t = this.getStyle(TOP);
6829         return {
6830             "position" : this.getStyle(POSITION),
6831             "left" : l,
6832             "right" : l ? "" : this.getStyle(RIGHT),
6833             "top" : t,
6834             "bottom" : t ? "" : this.getStyle(BOTTOM),
6835             "z-index" : this.getStyle(ZINDEX)
6836         };
6837     },
6838     
6839     /**
6840     * Set positioning with an object returned by getPositioning().
6841     * @param {Object} posCfg
6842     * @return {Ext.Element} this
6843      */
6844     setPositioning : function(pc){
6845             var me = this,
6846                 style = me.dom.style;
6847                 
6848         me.setStyle(pc);
6849         
6850         if(pc.right == AUTO){
6851             style.right = "";
6852         }
6853         if(pc.bottom == AUTO){
6854             style.bottom = "";
6855         }
6856         
6857         return me;
6858     },    
6859         
6860     /**
6861      * Translates the passed page coordinates into left/top css values for this element
6862      * @param {Number/Array} x The page x or an array containing [x, y]
6863      * @param {Number} y (optional) The page y, required if x is not an array
6864      * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
6865      */
6866     translatePoints : function(x, y){                
6867             y = isNaN(x[1]) ? y : x[1];
6868         x = isNaN(x[0]) ? x : x[0];
6869         var me = this,
6870                 relative = me.isStyle(POSITION, RELATIVE),
6871                 o = me.getXY(),
6872                 l = parseInt(me.getStyle(LEFT), 10),
6873                 t = parseInt(me.getStyle(TOP), 10);
6874         
6875         l = !isNaN(l) ? l : (relative ? 0 : me.dom.offsetLeft);
6876         t = !isNaN(t) ? t : (relative ? 0 : me.dom.offsetTop);        
6877
6878         return {left: (x - o[0] + l), top: (y - o[1] + t)}; 
6879     },
6880     
6881     animTest : function(args, animate, i) {
6882         return !!animate && this.preanim ? this.preanim(args, i) : false;
6883     }
6884 });
6885 })();/**
6886  * @class Ext.Element
6887  */
6888 Ext.Element.addMethods({
6889     /**
6890      * 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.
6891      * @param {Object} box The box to fill {x, y, width, height}
6892      * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
6893      * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
6894      * @return {Ext.Element} this
6895      */
6896     setBox : function(box, adjust, animate){
6897         var me = this,
6898                 w = box.width, 
6899                 h = box.height;
6900         if((adjust && !me.autoBoxAdjust) && !me.isBorderBox()){
6901            w -= (me.getBorderWidth("lr") + me.getPadding("lr"));
6902            h -= (me.getBorderWidth("tb") + me.getPadding("tb"));
6903         }
6904         me.setBounds(box.x, box.y, w, h, me.animTest.call(me, arguments, animate, 2));
6905         return me;
6906     },
6907
6908     /**
6909      * Return an object defining the area of this Element which can be passed to {@link #setBox} to
6910      * set another Element's size/location to match this element.
6911      * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
6912      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
6913      * @return {Object} box An object in the format<pre><code>
6914 {
6915     x: &lt;Element's X position>,
6916     y: &lt;Element's Y position>,
6917     width: &lt;Element's width>,
6918     height: &lt;Element's height>,
6919     bottom: &lt;Element's lower bound>,
6920     right: &lt;Element's rightmost bound>
6921 }
6922 </code></pre>
6923      * The returned object may also be addressed as an Array where index 0 contains the X position
6924      * and index 1 contains the Y position. So the result may also be used for {@link #setXY}
6925      */
6926         getBox : function(contentBox, local) {      
6927             var me = this,
6928                 xy,
6929                 left,
6930                 top,
6931                 getBorderWidth = me.getBorderWidth,
6932                 getPadding = me.getPadding, 
6933                 l,
6934                 r,
6935                 t,
6936                 b;
6937         if(!local){
6938             xy = me.getXY();
6939         }else{
6940             left = parseInt(me.getStyle("left"), 10) || 0;
6941             top = parseInt(me.getStyle("top"), 10) || 0;
6942             xy = [left, top];
6943         }
6944         var el = me.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
6945         if(!contentBox){
6946             bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
6947         }else{
6948             l = getBorderWidth.call(me, "l") + getPadding.call(me, "l");
6949             r = getBorderWidth.call(me, "r") + getPadding.call(me, "r");
6950             t = getBorderWidth.call(me, "t") + getPadding.call(me, "t");
6951             b = getBorderWidth.call(me, "b") + getPadding.call(me, "b");
6952             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)};
6953         }
6954         bx.right = bx.x + bx.width;
6955         bx.bottom = bx.y + bx.height;
6956         return bx;
6957         },
6958         
6959     /**
6960      * Move this element relative to its current position.
6961      * @param {String} direction Possible values are: "l" (or "left"), "r" (or "right"), "t" (or "top", or "up"), "b" (or "bottom", or "down").
6962      * @param {Number} distance How far to move the element in pixels
6963      * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
6964      * @return {Ext.Element} this
6965      */
6966      move : function(direction, distance, animate){
6967         var me = this,          
6968                 xy = me.getXY(),
6969                 x = xy[0],
6970                 y = xy[1],              
6971                 left = [x - distance, y],
6972                 right = [x + distance, y],
6973                 top = [x, y - distance],
6974                 bottom = [x, y + distance],
6975                 hash = {
6976                         l :     left,
6977                         left : left,
6978                         r : right,
6979                         right : right,
6980                         t : top,
6981                         top : top,
6982                         up : top,
6983                         b : bottom, 
6984                         bottom : bottom,
6985                         down : bottom                           
6986                 };
6987         
6988             direction = direction.toLowerCase();    
6989             me.moveTo(hash[direction][0], hash[direction][1], me.animTest.call(me, arguments, animate, 2));
6990     },
6991     
6992     /**
6993      * Quick set left and top adding default units
6994      * @param {String} left The left CSS property value
6995      * @param {String} top The top CSS property value
6996      * @return {Ext.Element} this
6997      */
6998      setLeftTop : function(left, top){
6999             var me = this,
7000                 style = me.dom.style;
7001         style.left = me.addUnits(left);
7002         style.top = me.addUnits(top);
7003         return me;
7004     },
7005     
7006     /**
7007      * Returns the region of the given element.
7008      * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7009      * @return {Region} A Ext.lib.Region containing "top, left, bottom, right" member data.
7010      */
7011     getRegion : function(){
7012         return Ext.lib.Dom.getRegion(this.dom);
7013     },
7014     
7015     /**
7016      * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
7017      * @param {Number} x X value for new position (coordinates are page-based)
7018      * @param {Number} y Y value for new position (coordinates are page-based)
7019      * @param {Mixed} width The new width. This may be one of:<div class="mdetail-params"><ul>
7020      * <li>A Number specifying the new width in this Element's {@link #defaultUnit}s (by default, pixels)</li>
7021      * <li>A String used to set the CSS width style. Animation may <b>not</b> be used.
7022      * </ul></div>
7023      * @param {Mixed} height The new height. This may be one of:<div class="mdetail-params"><ul>
7024      * <li>A Number specifying the new height in this Element's {@link #defaultUnit}s (by default, pixels)</li>
7025      * <li>A String used to set the CSS height style. Animation may <b>not</b> be used.</li>
7026      * </ul></div>
7027      * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7028      * @return {Ext.Element} this
7029      */
7030     setBounds : function(x, y, width, height, animate){
7031             var me = this;
7032         if (!animate || !me.anim) {
7033             me.setSize(width, height);
7034             me.setLocation(x, y);
7035         } else {
7036             me.anim({points: {to: [x, y]}, 
7037                          width: {to: me.adjustWidth(width)}, 
7038                          height: {to: me.adjustHeight(height)}},
7039                      me.preanim(arguments, 4), 
7040                      'motion');
7041         }
7042         return me;
7043     },
7044
7045     /**
7046      * Sets the element's position and size the specified region. If animation is true then width, height, x and y will be animated concurrently.
7047      * @param {Ext.lib.Region} region The region to fill
7048      * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7049      * @return {Ext.Element} this
7050      */
7051     setRegion : function(region, animate) {
7052         return this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.animTest.call(this, arguments, animate, 1));
7053     }
7054 });/**
7055  * @class Ext.Element
7056  */
7057 Ext.Element.addMethods({
7058     /**
7059      * Returns true if this element is scrollable.
7060      * @return {Boolean}
7061      */
7062     isScrollable : function(){
7063         var dom = this.dom;
7064         return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
7065     },
7066
7067     /**
7068      * 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().
7069      * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
7070      * @param {Number} value The new scroll value.
7071      * @return {Element} this
7072      */
7073     scrollTo : function(side, value){
7074         this.dom["scroll" + (/top/i.test(side) ? "Top" : "Left")] = value;
7075         return this;
7076     },
7077
7078     /**
7079      * Returns the current scroll position of the element.
7080      * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
7081      */
7082     getScroll : function(){
7083         var d = this.dom, 
7084             doc = document,
7085             body = doc.body,
7086             docElement = doc.documentElement,
7087             l,
7088             t,
7089             ret;
7090
7091         if(d == doc || d == body){
7092             if(Ext.isIE && Ext.isStrict){
7093                 l = docElement.scrollLeft; 
7094                 t = docElement.scrollTop;
7095             }else{
7096                 l = window.pageXOffset;
7097                 t = window.pageYOffset;
7098             }
7099             ret = {left: l || (body ? body.scrollLeft : 0), top: t || (body ? body.scrollTop : 0)};
7100         }else{
7101             ret = {left: d.scrollLeft, top: d.scrollTop};
7102         }
7103         return ret;
7104     }
7105 });/**
7106  * @class Ext.Element
7107  */
7108 Ext.Element.addMethods({
7109     /**
7110      * 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().
7111      * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
7112      * @param {Number} value The new scroll value
7113      * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7114      * @return {Element} this
7115      */
7116     scrollTo : function(side, value, animate){
7117         var top = /top/i.test(side), //check if we're scrolling top or left
7118                 me = this,
7119                 dom = me.dom,
7120             prop;
7121         if (!animate || !me.anim) {
7122             prop = 'scroll' + (top ? 'Top' : 'Left'), // just setting the value, so grab the direction
7123             dom[prop] = value;
7124         }else{
7125             prop = 'scroll' + (top ? 'Left' : 'Top'), // if scrolling top, we need to grab scrollLeft, if left, scrollTop
7126             me.anim({scroll: {to: top ? [dom[prop], value] : [value, dom[prop]]}},
7127                          me.preanim(arguments, 2), 'scroll');
7128         }
7129         return me;
7130     },
7131     
7132     /**
7133      * Scrolls this element into view within the passed container.
7134      * @param {Mixed} container (optional) The container element to scroll (defaults to document.body).  Should be a
7135      * string (id), dom node, or Ext.Element.
7136      * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7137      * @return {Ext.Element} this
7138      */
7139     scrollIntoView : function(container, hscroll){
7140         var c = Ext.getDom(container) || Ext.getBody().dom,
7141                 el = this.dom,
7142                 o = this.getOffsetsTo(c),
7143             l = o[0] + c.scrollLeft,
7144             t = o[1] + c.scrollTop,
7145             b = t + el.offsetHeight,
7146             r = l + el.offsetWidth,
7147                 ch = c.clientHeight,
7148                 ct = parseInt(c.scrollTop, 10),
7149                 cl = parseInt(c.scrollLeft, 10),
7150                 cb = ct + ch,
7151                 cr = cl + c.clientWidth;
7152
7153         if (el.offsetHeight > ch || t < ct) {
7154                 c.scrollTop = t;
7155         } else if (b > cb){
7156             c.scrollTop = b-ch;
7157         }
7158         c.scrollTop = c.scrollTop; // corrects IE, other browsers will ignore
7159
7160         if(hscroll !== false){
7161                         if(el.offsetWidth > c.clientWidth || l < cl){
7162                 c.scrollLeft = l;
7163             }else if(r > cr){
7164                 c.scrollLeft = r - c.clientWidth;
7165             }
7166             c.scrollLeft = c.scrollLeft;
7167         }
7168         return this;
7169     },
7170
7171     // private
7172     scrollChildIntoView : function(child, hscroll){
7173         Ext.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7174     },
7175     
7176     /**
7177      * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
7178      * within this element's scrollable range.
7179      * @param {String} direction Possible values are: "l" (or "left"), "r" (or "right"), "t" (or "top", or "up"), "b" (or "bottom", or "down").
7180      * @param {Number} distance How far to scroll the element in pixels
7181      * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7182      * @return {Boolean} Returns true if a scroll was triggered or false if the element
7183      * was scrolled as far as it could go.
7184      */
7185      scroll : function(direction, distance, animate){
7186          if(!this.isScrollable()){
7187              return;
7188          }
7189          var el = this.dom,
7190             l = el.scrollLeft, t = el.scrollTop,
7191             w = el.scrollWidth, h = el.scrollHeight,
7192             cw = el.clientWidth, ch = el.clientHeight,
7193             scrolled = false, v,
7194             hash = {
7195                 l: Math.min(l + distance, w-cw),
7196                 r: v = Math.max(l - distance, 0),
7197                 t: Math.max(t - distance, 0),
7198                 b: Math.min(t + distance, h-ch)
7199             };
7200             hash.d = hash.b;
7201             hash.u = hash.t;
7202             
7203          direction = direction.substr(0, 1);
7204          if((v = hash[direction]) > -1){
7205             scrolled = true;
7206             this.scrollTo(direction == 'l' || direction == 'r' ? 'left' : 'top', v, this.preanim(arguments, 2));
7207          }
7208          return scrolled;
7209     }
7210 });/**
7211  * @class Ext.Element
7212  */
7213 /**
7214  * Visibility mode constant for use with {@link #setVisibilityMode}. Use visibility to hide element
7215  * @static
7216  * @type Number
7217  */
7218 Ext.Element.VISIBILITY = 1;
7219 /**
7220  * Visibility mode constant for use with {@link #setVisibilityMode}. Use display to hide element
7221  * @static
7222  * @type Number
7223  */
7224 Ext.Element.DISPLAY = 2;
7225
7226 Ext.Element.addMethods(function(){
7227     var VISIBILITY = "visibility",
7228         DISPLAY = "display",
7229         HIDDEN = "hidden",
7230         OFFSETS = "offsets",
7231         NONE = "none",
7232         ORIGINALDISPLAY = 'originalDisplay',
7233         VISMODE = 'visibilityMode',
7234         ELDISPLAY = Ext.Element.DISPLAY,
7235         data = Ext.Element.data,
7236         getDisplay = function(dom){
7237             var d = data(dom, ORIGINALDISPLAY);
7238             if(d === undefined){
7239                 data(dom, ORIGINALDISPLAY, d = '');
7240             }
7241             return d;
7242         },
7243         getVisMode = function(dom){
7244             var m = data(dom, VISMODE);
7245             if(m === undefined){
7246                 data(dom, VISMODE, m = 1);
7247             }
7248             return m;
7249         };
7250
7251     return {
7252         /**
7253          * The element's default display mode  (defaults to "")
7254          * @type String
7255          */
7256         originalDisplay : "",
7257         visibilityMode : 1,
7258
7259         /**
7260          * Sets the element's visibility mode. When setVisible() is called it
7261          * will use this to determine whether to set the visibility or the display property.
7262          * @param {Number} visMode Ext.Element.VISIBILITY or Ext.Element.DISPLAY
7263          * @return {Ext.Element} this
7264          */
7265         setVisibilityMode : function(visMode){
7266             data(this.dom, VISMODE, visMode);
7267             return this;
7268         },
7269
7270         /**
7271          * Perform custom animation on this element.
7272          * <div><ul class="mdetail-params">
7273          * <li><u>Animation Properties</u></li>
7274          *
7275          * <p>The Animation Control Object enables gradual transitions for any member of an
7276          * element's style object that takes a numeric value including but not limited to
7277          * these properties:</p><div><ul class="mdetail-params">
7278          * <li><tt>bottom, top, left, right</tt></li>
7279          * <li><tt>height, width</tt></li>
7280          * <li><tt>margin, padding</tt></li>
7281          * <li><tt>borderWidth</tt></li>
7282          * <li><tt>opacity</tt></li>
7283          * <li><tt>fontSize</tt></li>
7284          * <li><tt>lineHeight</tt></li>
7285          * </ul></div>
7286          *
7287          *
7288          * <li><u>Animation Property Attributes</u></li>
7289          *
7290          * <p>Each Animation Property is a config object with optional properties:</p>
7291          * <div><ul class="mdetail-params">
7292          * <li><tt>by</tt>*  : relative change - start at current value, change by this value</li>
7293          * <li><tt>from</tt> : ignore current value, start from this value</li>
7294          * <li><tt>to</tt>*  : start at current value, go to this value</li>
7295          * <li><tt>unit</tt> : any allowable unit specification</li>
7296          * <p>* do not specify both <tt>to</tt> and <tt>by</tt> for an animation property</p>
7297          * </ul></div>
7298          *
7299          * <li><u>Animation Types</u></li>
7300          *
7301          * <p>The supported animation types:</p><div><ul class="mdetail-params">
7302          * <li><tt>'run'</tt> : Default
7303          * <pre><code>
7304 var el = Ext.get('complexEl');
7305 el.animate(
7306     // animation control object
7307     {
7308         borderWidth: {to: 3, from: 0},
7309         opacity: {to: .3, from: 1},
7310         height: {to: 50, from: el.getHeight()},
7311         width: {to: 300, from: el.getWidth()},
7312         top  : {by: - 100, unit: 'px'},
7313     },
7314     0.35,      // animation duration
7315     null,      // callback
7316     'easeOut', // easing method
7317     'run'      // animation type ('run','color','motion','scroll')
7318 );
7319          * </code></pre>
7320          * </li>
7321          * <li><tt>'color'</tt>
7322          * <p>Animates transition of background, text, or border colors.</p>
7323          * <pre><code>
7324 el.animate(
7325     // animation control object
7326     {
7327         color: { to: '#06e' },
7328         backgroundColor: { to: '#e06' }
7329     },
7330     0.35,      // animation duration
7331     null,      // callback
7332     'easeOut', // easing method
7333     'color'    // animation type ('run','color','motion','scroll')
7334 );
7335          * </code></pre>
7336          * </li>
7337          *
7338          * <li><tt>'motion'</tt>
7339          * <p>Animates the motion of an element to/from specific points using optional bezier
7340          * way points during transit.</p>
7341          * <pre><code>
7342 el.animate(
7343     // animation control object
7344     {
7345         borderWidth: {to: 3, from: 0},
7346         opacity: {to: .3, from: 1},
7347         height: {to: 50, from: el.getHeight()},
7348         width: {to: 300, from: el.getWidth()},
7349         top  : {by: - 100, unit: 'px'},
7350         points: {
7351             to: [50, 100],  // go to this point
7352             control: [      // optional bezier way points
7353                 [ 600, 800],
7354                 [-100, 200]
7355             ]
7356         }
7357     },
7358     3000,      // animation duration (milliseconds!)
7359     null,      // callback
7360     'easeOut', // easing method
7361     'motion'   // animation type ('run','color','motion','scroll')
7362 );
7363          * </code></pre>
7364          * </li>
7365          * <li><tt>'scroll'</tt>
7366          * <p>Animate horizontal or vertical scrolling of an overflowing page element.</p>
7367          * <pre><code>
7368 el.animate(
7369     // animation control object
7370     {
7371         scroll: {to: [400, 300]}
7372     },
7373     0.35,      // animation duration
7374     null,      // callback
7375     'easeOut', // easing method
7376     'scroll'   // animation type ('run','color','motion','scroll')
7377 );
7378          * </code></pre>
7379          * </li>
7380          * </ul></div>
7381          *
7382          * </ul></div>
7383          *
7384          * @param {Object} args The animation control args
7385          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to <tt>.35</tt>)
7386          * @param {Function} onComplete (optional) Function to call when animation completes
7387          * @param {String} easing (optional) {@link Ext.Fx#easing} method to use (defaults to <tt>'easeOut'</tt>)
7388          * @param {String} animType (optional) <tt>'run'</tt> is the default. Can also be <tt>'color'</tt>,
7389          * <tt>'motion'</tt>, or <tt>'scroll'</tt>
7390          * @return {Ext.Element} this
7391          */
7392         animate : function(args, duration, onComplete, easing, animType){
7393             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7394             return this;
7395         },
7396
7397         /*
7398          * @private Internal animation call
7399          */
7400         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7401             animType = animType || 'run';
7402             opt = opt || {};
7403             var me = this,
7404                 anim = Ext.lib.Anim[animType](
7405                     me.dom,
7406                     args,
7407                     (opt.duration || defaultDur) || .35,
7408                     (opt.easing || defaultEase) || 'easeOut',
7409                     function(){
7410                         if(cb) cb.call(me);
7411                         if(opt.callback) opt.callback.call(opt.scope || me, me, opt);
7412                     },
7413                     me
7414                 );
7415             opt.anim = anim;
7416             return anim;
7417         },
7418
7419         // private legacy anim prep
7420         preanim : function(a, i){
7421             return !a[i] ? false : (Ext.isObject(a[i]) ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7422         },
7423
7424         /**
7425          * Checks whether the element is currently visible using both visibility and display properties.
7426          * @return {Boolean} True if the element is currently visible, else false
7427          */
7428         isVisible : function() {
7429             return !this.isStyle(VISIBILITY, HIDDEN) && !this.isStyle(DISPLAY, NONE);
7430         },
7431
7432         /**
7433          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7434          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7435          * @param {Boolean} visible Whether the element is visible
7436          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7437          * @return {Ext.Element} this
7438          */
7439          setVisible : function(visible, animate){
7440             var me = this, isDisplay, isVisible, isOffsets,
7441                 dom = me.dom;
7442
7443             // hideMode string override
7444             if (Ext.isString(animate)){
7445                 isDisplay = animate == DISPLAY;
7446                 isVisible = animate == VISIBILITY;
7447                 isOffsets = animate == OFFSETS;
7448                 animate = false;
7449             } else {
7450                 isDisplay = getVisMode(this.dom) == ELDISPLAY;
7451                 isVisible = !isDisplay;
7452             }
7453
7454             if (!animate || !me.anim) {
7455                 if (isDisplay){
7456                     me.setDisplayed(visible);
7457                 } else if (isOffsets){
7458                     if (!visible){
7459                         me.hideModeStyles = {
7460                             position: me.getStyle('position'),
7461                             top: me.getStyle('top'),
7462                             left: me.getStyle('left')
7463                         };
7464
7465                         me.applyStyles({position: 'absolute', top: '-10000px', left: '-10000px'});
7466                     } else {
7467                         me.applyStyles(me.hideModeStyles || {position: '', top: '', left: ''});
7468                     }
7469                 }else{
7470                     me.fixDisplay();
7471                     dom.style.visibility = visible ? "visible" : HIDDEN;
7472                 }
7473             }else{
7474                 // closure for composites
7475                 if (visible){
7476                     me.setOpacity(.01);
7477                     me.setVisible(true);
7478                 }
7479                 me.anim({opacity: { to: (visible?1:0) }},
7480                         me.preanim(arguments, 1),
7481                         null,
7482                         .35,
7483                         'easeIn',
7484                         function(){
7485                              if(!visible){
7486                                  dom.style[isDisplay ? DISPLAY : VISIBILITY] = (isDisplay) ? NONE : HIDDEN;
7487                                  Ext.fly(dom).setOpacity(1);
7488                              }
7489                         });
7490             }
7491             return me;
7492         },
7493
7494         /**
7495          * Toggles the element's visibility or display, depending on visibility mode.
7496          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7497          * @return {Ext.Element} this
7498          */
7499         toggle : function(animate){
7500             var me = this;
7501             me.setVisible(!me.isVisible(), me.preanim(arguments, 0));
7502             return me;
7503         },
7504
7505         /**
7506          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7507          * @param {Mixed} value Boolean value to display the element using its default display, or a string to set the display directly.
7508          * @return {Ext.Element} this
7509          */
7510         setDisplayed : function(value) {
7511             if(typeof value == "boolean"){
7512                value = value ? getDisplay(this.dom) : NONE;
7513             }
7514             this.setStyle(DISPLAY, value);
7515             return this;
7516         },
7517
7518         // private
7519         fixDisplay : function(){
7520             var me = this;
7521             if(me.isStyle(DISPLAY, NONE)){
7522                 me.setStyle(VISIBILITY, HIDDEN);
7523                 me.setStyle(DISPLAY, getDisplay(this.dom)); // first try reverting to default
7524                 if(me.isStyle(DISPLAY, NONE)){ // if that fails, default to block
7525                     me.setStyle(DISPLAY, "block");
7526                 }
7527             }
7528         },
7529
7530         /**
7531          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
7532          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7533          * @return {Ext.Element} this
7534          */
7535         hide : function(animate){
7536             // hideMode override
7537             if (Ext.isString(animate)){
7538                 this.setVisible(false, animate);
7539                 return this;
7540             }
7541             this.setVisible(false, this.preanim(arguments, 0));
7542             return this;
7543         },
7544
7545         /**
7546         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
7547         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7548          * @return {Ext.Element} this
7549          */
7550         show : function(animate){
7551             // hideMode override
7552             if (Ext.isString(animate)){
7553                 this.setVisible(true, animate);
7554                 return this;
7555             }
7556             this.setVisible(true, this.preanim(arguments, 0));
7557             return this;
7558         }
7559     };
7560 }());/**
7561  * @class Ext.Element
7562  */
7563 Ext.Element.addMethods(
7564 function(){
7565     var VISIBILITY = "visibility",
7566         DISPLAY = "display",
7567         HIDDEN = "hidden",
7568         NONE = "none",
7569             XMASKED = "x-masked",
7570                 XMASKEDRELATIVE = "x-masked-relative",
7571         data = Ext.Element.data;
7572
7573         return {
7574                 /**
7575              * Checks whether the element is currently visible using both visibility and display properties.
7576              * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7577              * @return {Boolean} True if the element is currently visible, else false
7578              */
7579             isVisible : function(deep) {
7580                 var vis = !this.isStyle(VISIBILITY,HIDDEN) && !this.isStyle(DISPLAY,NONE),
7581                         p = this.dom.parentNode;
7582                 if(deep !== true || !vis){
7583                     return vis;
7584                 }
7585                 while(p && !/^body/i.test(p.tagName)){
7586                     if(!Ext.fly(p, '_isVisible').isVisible()){
7587                         return false;
7588                     }
7589                     p = p.parentNode;
7590                 }
7591                 return true;
7592             },
7593
7594             /**
7595              * Returns true if display is not "none"
7596              * @return {Boolean}
7597              */
7598             isDisplayed : function() {
7599                 return !this.isStyle(DISPLAY, NONE);
7600             },
7601
7602                 /**
7603              * Convenience method for setVisibilityMode(Element.DISPLAY)
7604              * @param {String} display (optional) What to set display to when visible
7605              * @return {Ext.Element} this
7606              */
7607             enableDisplayMode : function(display){
7608                 this.setVisibilityMode(Ext.Element.DISPLAY);
7609                 if(!Ext.isEmpty(display)){
7610                 data(this.dom, 'originalDisplay', display);
7611             }
7612                 return this;
7613             },
7614
7615                 /**
7616              * Puts a mask over this element to disable user interaction. Requires core.css.
7617              * This method can only be applied to elements which accept child nodes.
7618              * @param {String} msg (optional) A message to display in the mask
7619              * @param {String} msgCls (optional) A css class to apply to the msg element
7620              * @return {Element} The mask element
7621              */
7622             mask : function(msg, msgCls){
7623                     var me = this,
7624                         dom = me.dom,
7625                         dh = Ext.DomHelper,
7626                         EXTELMASKMSG = "ext-el-mask-msg",
7627                 el,
7628                 mask;
7629
7630                 if(!/^body/i.test(dom.tagName) && me.getStyle('position') == 'static'){
7631                     me.addClass(XMASKEDRELATIVE);
7632                 }
7633                 if((el = data(dom, 'maskMsg'))){
7634                     el.remove();
7635                 }
7636                 if((el = data(dom, 'mask'))){
7637                     el.remove();
7638                 }
7639
7640             mask = dh.append(dom, {cls : "ext-el-mask"}, true);
7641                 data(dom, 'mask', mask);
7642
7643                 me.addClass(XMASKED);
7644                 mask.setDisplayed(true);
7645                 if(typeof msg == 'string'){
7646                 var mm = dh.append(dom, {cls : EXTELMASKMSG, cn:{tag:'div'}}, true);
7647                 data(dom, 'maskMsg', mm);
7648                     mm.dom.className = msgCls ? EXTELMASKMSG + " " + msgCls : EXTELMASKMSG;
7649                     mm.dom.firstChild.innerHTML = msg;
7650                     mm.setDisplayed(true);
7651                     mm.center(me);
7652                 }
7653                 if(Ext.isIE && !(Ext.isIE7 && Ext.isStrict) && me.getStyle('height') == 'auto'){ // ie will not expand full height automatically
7654                     mask.setSize(undefined, me.getHeight());
7655                 }
7656                 return mask;
7657             },
7658
7659             /**
7660              * Removes a previously applied mask.
7661              */
7662             unmask : function(){
7663                     var me = this,
7664                 dom = me.dom,
7665                         mask = data(dom, 'mask'),
7666                         maskMsg = data(dom, 'maskMsg');
7667                 if(mask){
7668                     if(maskMsg){
7669                         maskMsg.remove();
7670                     data(dom, 'maskMsg', undefined);
7671                     }
7672                     mask.remove();
7673                 data(dom, 'mask', undefined);
7674                 }
7675                 me.removeClass([XMASKED, XMASKEDRELATIVE]);
7676             },
7677
7678             /**
7679              * Returns true if this element is masked
7680              * @return {Boolean}
7681              */
7682             isMasked : function(){
7683             var m = data(this.dom, 'mask');
7684                 return m && m.isVisible();
7685             },
7686
7687             /**
7688              * Creates an iframe shim for this element to keep selects and other windowed objects from
7689              * showing through.
7690              * @return {Ext.Element} The new shim element
7691              */
7692             createShim : function(){
7693                 var el = document.createElement('iframe'),
7694                         shim;
7695                 el.frameBorder = '0';
7696                 el.className = 'ext-shim';
7697                 el.src = Ext.SSL_SECURE_URL;
7698                 shim = Ext.get(this.dom.parentNode.insertBefore(el, this.dom));
7699                 shim.autoBoxAdjust = false;
7700                 return shim;
7701             }
7702     };
7703 }());/**
7704  * @class Ext.Element
7705  */
7706 Ext.Element.addMethods({
7707     /**
7708      * Convenience method for constructing a KeyMap
7709      * @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:
7710      * <code>{key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}</code>
7711      * @param {Function} fn The function to call
7712      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the specified function is executed. Defaults to this Element.
7713      * @return {Ext.KeyMap} The KeyMap created
7714      */
7715     addKeyListener : function(key, fn, scope){
7716         var config;
7717         if(!Ext.isObject(key) || Ext.isArray(key)){
7718             config = {
7719                 key: key,
7720                 fn: fn,
7721                 scope: scope
7722             };
7723         }else{
7724             config = {
7725                 key : key.key,
7726                 shift : key.shift,
7727                 ctrl : key.ctrl,
7728                 alt : key.alt,
7729                 fn: fn,
7730                 scope: scope
7731             };
7732         }
7733         return new Ext.KeyMap(this, config);
7734     },
7735
7736     /**
7737      * Creates a KeyMap for this element
7738      * @param {Object} config The KeyMap config. See {@link Ext.KeyMap} for more details
7739      * @return {Ext.KeyMap} The KeyMap created
7740      */
7741     addKeyMap : function(config){
7742         return new Ext.KeyMap(this, config);
7743     }
7744 });(function(){
7745     // contants
7746     var NULL = null,
7747         UNDEFINED = undefined,
7748         TRUE = true,
7749         FALSE = false,
7750         SETX = "setX",
7751         SETY = "setY",
7752         SETXY = "setXY",
7753         LEFT = "left",
7754         BOTTOM = "bottom",
7755         TOP = "top",
7756         RIGHT = "right",
7757         HEIGHT = "height",
7758         WIDTH = "width",
7759         POINTS = "points",
7760         HIDDEN = "hidden",
7761         ABSOLUTE = "absolute",
7762         VISIBLE = "visible",
7763         MOTION = "motion",
7764         POSITION = "position",
7765         EASEOUT = "easeOut",
7766         /*
7767          * Use a light flyweight here since we are using so many callbacks and are always assured a DOM element
7768          */
7769         flyEl = new Ext.Element.Flyweight(),
7770         queues = {},
7771         getObject = function(o){
7772             return o || {};
7773         },
7774         fly = function(dom){
7775             flyEl.dom = dom;
7776             flyEl.id = Ext.id(dom);
7777             return flyEl;
7778         },
7779         /*
7780          * Queueing now stored outside of the element due to closure issues
7781          */
7782         getQueue = function(id){
7783             if(!queues[id]){
7784                 queues[id] = [];
7785             }
7786             return queues[id];
7787         },
7788         setQueue = function(id, value){
7789             queues[id] = value;
7790         };
7791         
7792 //Notifies Element that fx methods are available
7793 Ext.enableFx = TRUE;
7794
7795 /**
7796  * @class Ext.Fx
7797  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
7798  * to the {@link Ext.Element} interface when included, so all effects calls should be performed via {@link Ext.Element}.
7799  * Conversely, since the effects are not actually defined in {@link Ext.Element}, Ext.Fx <b>must</b> be
7800  * {@link Ext#enableFx included} in order for the Element effects to work.</p><br/>
7801  * 
7802  * <p><b><u>Method Chaining</u></b></p>
7803  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
7804  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
7805  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
7806  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
7807  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
7808  * expected results and should be done with care.  Also see <tt>{@link #callback}</tt>.</p><br/>
7809  *
7810  * <p><b><u>Anchor Options for Motion Effects</u></b></p>
7811  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
7812  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
7813 <pre>
7814 Value  Description
7815 -----  -----------------------------
7816 tl     The top left corner
7817 t      The center of the top edge
7818 tr     The top right corner
7819 l      The center of the left edge
7820 r      The center of the right edge
7821 bl     The bottom left corner
7822 b      The center of the bottom edge
7823 br     The bottom right corner
7824 </pre>
7825  * <b>Note</b>: some Fx methods accept specific custom config parameters.  The options shown in the Config Options
7826  * section below are common options that can be passed to any Fx method unless otherwise noted.</b>
7827  * 
7828  * @cfg {Function} callback A function called when the effect is finished.  Note that effects are queued internally by the
7829  * Fx class, so a callback is not required to specify another effect -- effects can simply be chained together
7830  * and called in sequence (see note for <b><u>Method Chaining</u></b> above), for example:<pre><code>
7831  * el.slideIn().highlight();
7832  * </code></pre>
7833  * The callback is intended for any additional code that should run once a particular effect has completed. The Element
7834  * being operated upon is passed as the first parameter.
7835  * 
7836  * @cfg {Object} scope The scope (<code>this</code> reference) in which the <tt>{@link #callback}</tt> function is executed. Defaults to the browser window.
7837  * 
7838  * @cfg {String} easing A valid Ext.lib.Easing value for the effect:</p><div class="mdetail-params"><ul>
7839  * <li><b><tt>backBoth</tt></b></li>
7840  * <li><b><tt>backIn</tt></b></li>
7841  * <li><b><tt>backOut</tt></b></li>
7842  * <li><b><tt>bounceBoth</tt></b></li>
7843  * <li><b><tt>bounceIn</tt></b></li>
7844  * <li><b><tt>bounceOut</tt></b></li>
7845  * <li><b><tt>easeBoth</tt></b></li>
7846  * <li><b><tt>easeBothStrong</tt></b></li>
7847  * <li><b><tt>easeIn</tt></b></li>
7848  * <li><b><tt>easeInStrong</tt></b></li>
7849  * <li><b><tt>easeNone</tt></b></li>
7850  * <li><b><tt>easeOut</tt></b></li>
7851  * <li><b><tt>easeOutStrong</tt></b></li>
7852  * <li><b><tt>elasticBoth</tt></b></li>
7853  * <li><b><tt>elasticIn</tt></b></li>
7854  * <li><b><tt>elasticOut</tt></b></li>
7855  * </ul></div>
7856  *
7857  * @cfg {String} afterCls A css class to apply after the effect
7858  * @cfg {Number} duration The length of time (in seconds) that the effect should last
7859  * 
7860  * @cfg {Number} endOpacity Only applicable for {@link #fadeIn} or {@link #fadeOut}, a number between
7861  * <tt>0</tt> and <tt>1</tt> inclusive to configure the ending opacity value.
7862  *  
7863  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
7864  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
7865  * effects that end with the element being visually hidden, ignored otherwise)
7866  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. <tt>"width:100px"</tt>, or an object
7867  * in the form <tt>{width:"100px"}</tt>, or a function which returns such a specification that will be applied to the
7868  * Element after the effect finishes.
7869  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
7870  * @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
7871  * @cfg {Boolean} stopFx Whether preceding effects should be stopped and removed before running current effect (only applies to non blocking effects)
7872  */
7873 Ext.Fx = {
7874     
7875     // private - calls the function taking arguments from the argHash based on the key.  Returns the return value of the function.
7876     //           this is useful for replacing switch statements (for example).
7877     switchStatements : function(key, fn, argHash){
7878         return fn.apply(this, argHash[key]);
7879     },
7880     
7881     /**
7882      * Slides the element into view.  An anchor point can be optionally passed to set the point of
7883      * origin for the slide effect.  This function automatically handles wrapping the element with
7884      * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
7885      * Usage:
7886      *<pre><code>
7887 // default: slide the element in from the top
7888 el.slideIn();
7889
7890 // custom: slide the element in from the right with a 2-second duration
7891 el.slideIn('r', { duration: 2 });
7892
7893 // common config options shown with default values
7894 el.slideIn('t', {
7895     easing: 'easeOut',
7896     duration: .5
7897 });
7898 </code></pre>
7899      * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
7900      * @param {Object} options (optional) Object literal with any of the Fx config options
7901      * @return {Ext.Element} The Element
7902      */
7903     slideIn : function(anchor, o){ 
7904         o = getObject(o);
7905         var me = this,
7906             dom = me.dom,
7907             st = dom.style,
7908             xy,
7909             r,
7910             b,              
7911             wrap,               
7912             after,
7913             st,
7914             args, 
7915             pt,
7916             bw,
7917             bh;
7918             
7919         anchor = anchor || "t";
7920
7921         me.queueFx(o, function(){            
7922             xy = fly(dom).getXY();
7923             // fix display to visibility
7924             fly(dom).fixDisplay();            
7925             
7926             // restore values after effect
7927             r = fly(dom).getFxRestore();      
7928             b = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: dom.offsetWidth, height: dom.offsetHeight};
7929             b.right = b.x + b.width;
7930             b.bottom = b.y + b.height;
7931             
7932             // fixed size for slide
7933             fly(dom).setWidth(b.width).setHeight(b.height);            
7934             
7935             // wrap if needed
7936             wrap = fly(dom).fxWrap(r.pos, o, HIDDEN);
7937             
7938             st.visibility = VISIBLE;
7939             st.position = ABSOLUTE;
7940             
7941             // clear out temp styles after slide and unwrap
7942             function after(){
7943                  fly(dom).fxUnwrap(wrap, r.pos, o);
7944                  st.width = r.width;
7945                  st.height = r.height;
7946                  fly(dom).afterFx(o);
7947             }
7948             
7949             // time to calculate the positions        
7950             pt = {to: [b.x, b.y]}; 
7951             bw = {to: b.width};
7952             bh = {to: b.height};
7953                 
7954             function argCalc(wrap, style, ww, wh, sXY, sXYval, s1, s2, w, h, p){                    
7955                 var ret = {};
7956                 fly(wrap).setWidth(ww).setHeight(wh);
7957                 if(fly(wrap)[sXY]){
7958                     fly(wrap)[sXY](sXYval);                  
7959                 }
7960                 style[s1] = style[s2] = "0";                    
7961                 if(w){
7962                     ret.width = w
7963                 };
7964                 if(h){
7965                     ret.height = h;
7966                 }
7967                 if(p){
7968                     ret.points = p;
7969                 }
7970                 return ret;
7971             };
7972
7973             args = fly(dom).switchStatements(anchor.toLowerCase(), argCalc, {
7974                     t  : [wrap, st, b.width, 0, NULL, NULL, LEFT, BOTTOM, NULL, bh, NULL],
7975                     l  : [wrap, st, 0, b.height, NULL, NULL, RIGHT, TOP, bw, NULL, NULL],
7976                     r  : [wrap, st, b.width, b.height, SETX, b.right, LEFT, TOP, NULL, NULL, pt],
7977                     b  : [wrap, st, b.width, b.height, SETY, b.bottom, LEFT, TOP, NULL, bh, pt],
7978                     tl : [wrap, st, 0, 0, NULL, NULL, RIGHT, BOTTOM, bw, bh, pt],
7979                     bl : [wrap, st, 0, 0, SETY, b.y + b.height, RIGHT, TOP, bw, bh, pt],
7980                     br : [wrap, st, 0, 0, SETXY, [b.right, b.bottom], LEFT, TOP, bw, bh, pt],
7981                     tr : [wrap, st, 0, 0, SETX, b.x + b.width, LEFT, BOTTOM, bw, bh, pt]
7982                 });
7983             
7984             st.visibility = VISIBLE;
7985             fly(wrap).show();
7986
7987             arguments.callee.anim = fly(wrap).fxanim(args,
7988                 o,
7989                 MOTION,
7990                 .5,
7991                 EASEOUT, 
7992                 after);
7993         });
7994         return me;
7995     },
7996     
7997     /**
7998      * Slides the element out of view.  An anchor point can be optionally passed to set the end point
7999      * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
8000      * 'hidden') but block elements will still take up space in the document.  The element must be removed
8001      * from the DOM using the 'remove' config option if desired.  This function automatically handles 
8002      * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
8003      * Usage:
8004      *<pre><code>
8005 // default: slide the element out to the top
8006 el.slideOut();
8007
8008 // custom: slide the element out to the right with a 2-second duration
8009 el.slideOut('r', { duration: 2 });
8010
8011 // common config options shown with default values
8012 el.slideOut('t', {
8013     easing: 'easeOut',
8014     duration: .5,
8015     remove: false,
8016     useDisplay: false
8017 });
8018 </code></pre>
8019      * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
8020      * @param {Object} options (optional) Object literal with any of the Fx config options
8021      * @return {Ext.Element} The Element
8022      */
8023     slideOut : function(anchor, o){
8024         o = getObject(o);
8025         var me = this,
8026             dom = me.dom,
8027             st = dom.style,
8028             xy = me.getXY(),
8029             wrap,
8030             r,
8031             b,
8032             a,
8033             zero = {to: 0}; 
8034                     
8035         anchor = anchor || "t";
8036
8037         me.queueFx(o, function(){
8038             
8039             // restore values after effect
8040             r = fly(dom).getFxRestore(); 
8041             b = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: dom.offsetWidth, height: dom.offsetHeight};
8042             b.right = b.x + b.width;
8043             b.bottom = b.y + b.height;
8044                 
8045             // fixed size for slide   
8046             fly(dom).setWidth(b.width).setHeight(b.height);
8047
8048             // wrap if needed
8049             wrap = fly(dom).fxWrap(r.pos, o, VISIBLE);
8050                 
8051             st.visibility = VISIBLE;
8052             st.position = ABSOLUTE;
8053             fly(wrap).setWidth(b.width).setHeight(b.height);            
8054
8055             function after(){
8056                 o.useDisplay ? fly(dom).setDisplayed(FALSE) : fly(dom).hide();                
8057                 fly(dom).fxUnwrap(wrap, r.pos, o);
8058                 st.width = r.width;
8059                 st.height = r.height;
8060                 fly(dom).afterFx(o);
8061             }            
8062             
8063             function argCalc(style, s1, s2, p1, v1, p2, v2, p3, v3){                    
8064                 var ret = {};
8065                 
8066                 style[s1] = style[s2] = "0";
8067                 ret[p1] = v1;               
8068                 if(p2){
8069                     ret[p2] = v2;               
8070                 }
8071                 if(p3){
8072                     ret[p3] = v3;
8073                 }
8074                 
8075                 return ret;
8076             };
8077             
8078             a = fly(dom).switchStatements(anchor.toLowerCase(), argCalc, {
8079                 t  : [st, LEFT, BOTTOM, HEIGHT, zero],
8080                 l  : [st, RIGHT, TOP, WIDTH, zero],
8081                 r  : [st, LEFT, TOP, WIDTH, zero, POINTS, {to : [b.right, b.y]}],
8082                 b  : [st, LEFT, TOP, HEIGHT, zero, POINTS, {to : [b.x, b.bottom]}],
8083                 tl : [st, RIGHT, BOTTOM, WIDTH, zero, HEIGHT, zero],
8084                 bl : [st, RIGHT, TOP, WIDTH, zero, HEIGHT, zero, POINTS, {to : [b.x, b.bottom]}],
8085                 br : [st, LEFT, TOP, WIDTH, zero, HEIGHT, zero, POINTS, {to : [b.x + b.width, b.bottom]}],
8086                 tr : [st, LEFT, BOTTOM, WIDTH, zero, HEIGHT, zero, POINTS, {to : [b.right, b.y]}]
8087             });
8088             
8089             arguments.callee.anim = fly(wrap).fxanim(a,
8090                 o,
8091                 MOTION,
8092                 .5,
8093                 EASEOUT, 
8094                 after);
8095         });
8096         return me;
8097     },
8098
8099     /**
8100      * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
8101      * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
8102      * The element must be removed from the DOM using the 'remove' config option if desired.
8103      * Usage:
8104      *<pre><code>
8105 // default
8106 el.puff();
8107
8108 // common config options shown with default values
8109 el.puff({
8110     easing: 'easeOut',
8111     duration: .5,
8112     remove: false,
8113     useDisplay: false
8114 });
8115 </code></pre>
8116      * @param {Object} options (optional) Object literal with any of the Fx config options
8117      * @return {Ext.Element} The Element
8118      */
8119     puff : function(o){
8120         o = getObject(o);
8121         var me = this,
8122             dom = me.dom,
8123             st = dom.style,
8124             width,
8125             height,
8126             r;
8127
8128         me.queueFx(o, function(){
8129             width = fly(dom).getWidth();
8130             height = fly(dom).getHeight();
8131             fly(dom).clearOpacity();
8132             fly(dom).show();
8133
8134             // restore values after effect
8135             r = fly(dom).getFxRestore();                   
8136             
8137             function after(){
8138                 o.useDisplay ? fly(dom).setDisplayed(FALSE) : fly(dom).hide();                  
8139                 fly(dom).clearOpacity();  
8140                 fly(dom).setPositioning(r.pos);
8141                 st.width = r.width;
8142                 st.height = r.height;
8143                 st.fontSize = '';
8144                 fly(dom).afterFx(o);
8145             }   
8146
8147             arguments.callee.anim = fly(dom).fxanim({
8148                     width : {to : fly(dom).adjustWidth(width * 2)},
8149                     height : {to : fly(dom).adjustHeight(height * 2)},
8150                     points : {by : [-width * .5, -height * .5]},
8151                     opacity : {to : 0},
8152                     fontSize: {to : 200, unit: "%"}
8153                 },
8154                 o,
8155                 MOTION,
8156                 .5,
8157                 EASEOUT,
8158                  after);
8159         });
8160         return me;
8161     },
8162
8163     /**
8164      * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
8165      * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
8166      * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
8167      * Usage:
8168      *<pre><code>
8169 // default
8170 el.switchOff();
8171
8172 // all config options shown with default values
8173 el.switchOff({
8174     easing: 'easeIn',
8175     duration: .3,
8176     remove: false,
8177     useDisplay: false
8178 });
8179 </code></pre>
8180      * @param {Object} options (optional) Object literal with any of the Fx config options
8181      * @return {Ext.Element} The Element
8182      */
8183     switchOff : function(o){
8184         o = getObject(o);
8185         var me = this,
8186             dom = me.dom,
8187             st = dom.style,
8188             r;
8189
8190         me.queueFx(o, function(){
8191             fly(dom).clearOpacity();
8192             fly(dom).clip();
8193
8194             // restore values after effect
8195             r = fly(dom).getFxRestore();
8196                 
8197             function after(){
8198                 o.useDisplay ? fly(dom).setDisplayed(FALSE) : fly(dom).hide();  
8199                 fly(dom).clearOpacity();
8200                 fly(dom).setPositioning(r.pos);
8201                 st.width = r.width;
8202                 st.height = r.height;   
8203                 fly(dom).afterFx(o);
8204             };
8205
8206             fly(dom).fxanim({opacity : {to : 0.3}}, 
8207                 NULL, 
8208                 NULL, 
8209                 .1, 
8210                 NULL, 
8211                 function(){                                 
8212                     fly(dom).clearOpacity();
8213                         (function(){                            
8214                             fly(dom).fxanim({
8215                                 height : {to : 1},
8216                                 points : {by : [0, fly(dom).getHeight() * .5]}
8217                             }, 
8218                             o, 
8219                             MOTION, 
8220                             0.3, 
8221                             'easeIn', 
8222                             after);
8223                         }).defer(100);
8224                 });
8225         });
8226         return me;
8227     },
8228
8229     /**
8230      * Highlights the Element by setting a color (applies to the background-color by default, but can be
8231      * changed using the "attr" config option) and then fading back to the original color. If no original
8232      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
8233      * Usage:
8234 <pre><code>
8235 // default: highlight background to yellow
8236 el.highlight();
8237
8238 // custom: highlight foreground text to blue for 2 seconds
8239 el.highlight("0000ff", { attr: 'color', duration: 2 });
8240
8241 // common config options shown with default values
8242 el.highlight("ffff9c", {
8243     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
8244     endColor: (current color) or "ffffff",
8245     easing: 'easeIn',
8246     duration: 1
8247 });
8248 </code></pre>
8249      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
8250      * @param {Object} options (optional) Object literal with any of the Fx config options
8251      * @return {Ext.Element} The Element
8252      */ 
8253     highlight : function(color, o){
8254         o = getObject(o);
8255         var me = this,
8256             dom = me.dom,
8257             attr = o.attr || "backgroundColor",
8258             a = {},
8259             restore;
8260
8261         me.queueFx(o, function(){
8262             fly(dom).clearOpacity();
8263             fly(dom).show();
8264
8265             function after(){
8266                 dom.style[attr] = restore;
8267                 fly(dom).afterFx(o);
8268             }            
8269             restore = dom.style[attr];
8270             a[attr] = {from: color || "ffff9c", to: o.endColor || fly(dom).getColor(attr) || "ffffff"};
8271             arguments.callee.anim = fly(dom).fxanim(a,
8272                 o,
8273                 'color',
8274                 1,
8275                 'easeIn', 
8276                 after);
8277         });
8278         return me;
8279     },
8280
8281    /**
8282     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
8283     * Usage:
8284 <pre><code>
8285 // default: a single light blue ripple
8286 el.frame();
8287
8288 // custom: 3 red ripples lasting 3 seconds total
8289 el.frame("ff0000", 3, { duration: 3 });
8290
8291 // common config options shown with default values
8292 el.frame("C3DAF9", 1, {
8293     duration: 1 //duration of each individual ripple.
8294     // Note: Easing is not configurable and will be ignored if included
8295 });
8296 </code></pre>
8297     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
8298     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
8299     * @param {Object} options (optional) Object literal with any of the Fx config options
8300     * @return {Ext.Element} The Element
8301     */
8302     frame : function(color, count, o){
8303         o = getObject(o);
8304         var me = this,
8305             dom = me.dom,
8306             proxy,
8307             active;
8308
8309         me.queueFx(o, function(){
8310             color = color || '#C3DAF9';
8311             if(color.length == 6){
8312                 color = '#' + color;
8313             }            
8314             count = count || 1;
8315             fly(dom).show();
8316
8317             var xy = fly(dom).getXY(),
8318                 b = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: dom.offsetWidth, height: dom.offsetHeight},
8319                 queue = function(){
8320                     proxy = fly(document.body || document.documentElement).createChild({
8321                         style:{
8322                             position : ABSOLUTE,
8323                             'z-index': 35000, // yee haw
8324                             border : '0px solid ' + color
8325                         }
8326                     });
8327                     return proxy.queueFx({}, animFn);
8328                 };
8329             
8330             
8331             arguments.callee.anim = {
8332                 isAnimated: true,
8333                 stop: function() {
8334                     count = 0;
8335                     proxy.stopFx();
8336                 }
8337             };
8338             
8339             function animFn(){
8340                 var scale = Ext.isBorderBox ? 2 : 1;
8341                 active = proxy.anim({
8342                     top : {from : b.y, to : b.y - 20},
8343                     left : {from : b.x, to : b.x - 20},
8344                     borderWidth : {from : 0, to : 10},
8345                     opacity : {from : 1, to : 0},
8346                     height : {from : b.height, to : b.height + 20 * scale},
8347                     width : {from : b.width, to : b.width + 20 * scale}
8348                 },{
8349                     duration: o.duration || 1,
8350                     callback: function() {
8351                         proxy.remove();
8352                         --count > 0 ? queue() : fly(dom).afterFx(o);
8353                     }
8354                 });
8355                 arguments.callee.anim = {
8356                     isAnimated: true,
8357                     stop: function(){
8358                         active.stop();
8359                     }
8360                 };
8361             };
8362             queue();
8363         });
8364         return me;
8365     },
8366
8367    /**
8368     * Creates a pause before any subsequent queued effects begin.  If there are
8369     * no effects queued after the pause it will have no effect.
8370     * Usage:
8371 <pre><code>
8372 el.pause(1);
8373 </code></pre>
8374     * @param {Number} seconds The length of time to pause (in seconds)
8375     * @return {Ext.Element} The Element
8376     */
8377     pause : function(seconds){        
8378         var dom = this.dom,
8379             t;
8380
8381         this.queueFx({}, function(){
8382             t = setTimeout(function(){
8383                 fly(dom).afterFx({});
8384             }, seconds * 1000);
8385             arguments.callee.anim = {
8386                 isAnimated: true,
8387                 stop: function(){
8388                     clearTimeout(t);
8389                     fly(dom).afterFx({});
8390                 }
8391             };
8392         });
8393         return this;
8394     },
8395
8396    /**
8397     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
8398     * using the <tt>{@link #endOpacity}</tt> config option.
8399     * Usage:
8400 <pre><code>
8401 // default: fade in from opacity 0 to 100%
8402 el.fadeIn();
8403
8404 // custom: fade in from opacity 0 to 75% over 2 seconds
8405 el.fadeIn({ endOpacity: .75, duration: 2});
8406
8407 // common config options shown with default values
8408 el.fadeIn({
8409     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
8410     easing: 'easeOut',
8411     duration: .5
8412 });
8413 </code></pre>
8414     * @param {Object} options (optional) Object literal with any of the Fx config options
8415     * @return {Ext.Element} The Element
8416     */
8417     fadeIn : function(o){
8418         o = getObject(o);
8419         var me = this,
8420             dom = me.dom,
8421             to = o.endOpacity || 1;
8422         
8423         me.queueFx(o, function(){
8424             fly(dom).setOpacity(0);
8425             fly(dom).fixDisplay();
8426             dom.style.visibility = VISIBLE;
8427             arguments.callee.anim = fly(dom).fxanim({opacity:{to:to}},
8428                 o, NULL, .5, EASEOUT, function(){
8429                 if(to == 1){
8430                     fly(dom).clearOpacity();
8431                 }
8432                 fly(dom).afterFx(o);
8433             });
8434         });
8435         return me;
8436     },
8437
8438    /**
8439     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
8440     * using the <tt>{@link #endOpacity}</tt> config option.  Note that IE may require
8441     * <tt>{@link #useDisplay}:true</tt> in order to redisplay correctly.
8442     * Usage:
8443 <pre><code>
8444 // default: fade out from the element's current opacity to 0
8445 el.fadeOut();
8446
8447 // custom: fade out from the element's current opacity to 25% over 2 seconds
8448 el.fadeOut({ endOpacity: .25, duration: 2});
8449
8450 // common config options shown with default values
8451 el.fadeOut({
8452     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
8453     easing: 'easeOut',
8454     duration: .5,
8455     remove: false,
8456     useDisplay: false
8457 });
8458 </code></pre>
8459     * @param {Object} options (optional) Object literal with any of the Fx config options
8460     * @return {Ext.Element} The Element
8461     */
8462     fadeOut : function(o){
8463         o = getObject(o);
8464         var me = this,
8465             dom = me.dom,
8466             style = dom.style,
8467             to = o.endOpacity || 0;         
8468         
8469         me.queueFx(o, function(){  
8470             arguments.callee.anim = fly(dom).fxanim({ 
8471                 opacity : {to : to}},
8472                 o, 
8473                 NULL, 
8474                 .5, 
8475                 EASEOUT, 
8476                 function(){
8477                     if(to == 0){
8478                         Ext.Element.data(dom, 'visibilityMode') == Ext.Element.DISPLAY || o.useDisplay ? 
8479                             style.display = "none" :
8480                             style.visibility = HIDDEN;
8481                             
8482                         fly(dom).clearOpacity();
8483                     }
8484                     fly(dom).afterFx(o);
8485             });
8486         });
8487         return me;
8488     },
8489
8490    /**
8491     * Animates the transition of an element's dimensions from a starting height/width
8492     * to an ending height/width.  This method is a convenience implementation of {@link shift}.
8493     * Usage:
8494 <pre><code>
8495 // change height and width to 100x100 pixels
8496 el.scale(100, 100);
8497
8498 // common config options shown with default values.  The height and width will default to
8499 // the element&#39;s existing values if passed as null.
8500 el.scale(
8501     [element&#39;s width],
8502     [element&#39;s height], {
8503         easing: 'easeOut',
8504         duration: .35
8505     }
8506 );
8507 </code></pre>
8508     * @param {Number} width  The new width (pass undefined to keep the original width)
8509     * @param {Number} height  The new height (pass undefined to keep the original height)
8510     * @param {Object} options (optional) Object literal with any of the Fx config options
8511     * @return {Ext.Element} The Element
8512     */
8513     scale : function(w, h, o){
8514         this.shift(Ext.apply({}, o, {
8515             width: w,
8516             height: h
8517         }));
8518         return this;
8519     },
8520
8521    /**
8522     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
8523     * Any of these properties not specified in the config object will not be changed.  This effect 
8524     * requires that at least one new dimension, position or opacity setting must be passed in on
8525     * the config object in order for the function to have any effect.
8526     * Usage:
8527 <pre><code>
8528 // slide the element horizontally to x position 200 while changing the height and opacity
8529 el.shift({ x: 200, height: 50, opacity: .8 });
8530
8531 // common config options shown with default values.
8532 el.shift({
8533     width: [element&#39;s width],
8534     height: [element&#39;s height],
8535     x: [element&#39;s x position],
8536     y: [element&#39;s y position],
8537     opacity: [element&#39;s opacity],
8538     easing: 'easeOut',
8539     duration: .35
8540 });
8541 </code></pre>
8542     * @param {Object} options  Object literal with any of the Fx config options
8543     * @return {Ext.Element} The Element
8544     */
8545     shift : function(o){
8546         o = getObject(o);
8547         var dom = this.dom,
8548             a = {};
8549                 
8550         this.queueFx(o, function(){
8551             for (var prop in o) {
8552                 if (o[prop] != UNDEFINED) {                                                 
8553                     a[prop] = {to : o[prop]};                   
8554                 }
8555             } 
8556             
8557             a.width ? a.width.to = fly(dom).adjustWidth(o.width) : a;
8558             a.height ? a.height.to = fly(dom).adjustWidth(o.height) : a;   
8559             
8560             if (a.x || a.y || a.xy) {
8561                 a.points = a.xy || 
8562                            {to : [ a.x ? a.x.to : fly(dom).getX(),
8563                                    a.y ? a.y.to : fly(dom).getY()]};                  
8564             }
8565
8566             arguments.callee.anim = fly(dom).fxanim(a,
8567                 o, 
8568                 MOTION, 
8569                 .35, 
8570                 EASEOUT, 
8571                 function(){
8572                     fly(dom).afterFx(o);
8573                 });
8574         });
8575         return this;
8576     },
8577
8578     /**
8579      * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
8580      * ending point of the effect.
8581      * Usage:
8582      *<pre><code>
8583 // default: slide the element downward while fading out
8584 el.ghost();
8585
8586 // custom: slide the element out to the right with a 2-second duration
8587 el.ghost('r', { duration: 2 });
8588
8589 // common config options shown with default values
8590 el.ghost('b', {
8591     easing: 'easeOut',
8592     duration: .5,
8593     remove: false,
8594     useDisplay: false
8595 });
8596 </code></pre>
8597      * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
8598      * @param {Object} options (optional) Object literal with any of the Fx config options
8599      * @return {Ext.Element} The Element
8600      */
8601     ghost : function(anchor, o){
8602         o = getObject(o);
8603         var me = this,
8604             dom = me.dom,
8605             st = dom.style,
8606             a = {opacity: {to: 0}, points: {}},
8607             pt = a.points,
8608             r,
8609             w,
8610             h;
8611             
8612         anchor = anchor || "b";
8613
8614         me.queueFx(o, function(){
8615             // restore values after effect
8616             r = fly(dom).getFxRestore();
8617             w = fly(dom).getWidth();
8618             h = fly(dom).getHeight();
8619             
8620             function after(){
8621                 o.useDisplay ? fly(dom).setDisplayed(FALSE) : fly(dom).hide();   
8622                 fly(dom).clearOpacity();
8623                 fly(dom).setPositioning(r.pos);
8624                 st.width = r.width;
8625                 st.height = r.height;
8626                 fly(dom).afterFx(o);
8627             }
8628                 
8629             pt.by = fly(dom).switchStatements(anchor.toLowerCase(), function(v1,v2){ return [v1, v2];}, {
8630                t  : [0, -h],
8631                l  : [-w, 0],
8632                r  : [w, 0],
8633                b  : [0, h],
8634                tl : [-w, -h],
8635                bl : [-w, h],
8636                br : [w, h],
8637                tr : [w, -h] 
8638             });
8639                 
8640             arguments.callee.anim = fly(dom).fxanim(a,
8641                 o,
8642                 MOTION,
8643                 .5,
8644                 EASEOUT, after);
8645         });
8646         return me;
8647     },
8648
8649     /**
8650      * Ensures that all effects queued after syncFx is called on the element are
8651      * run concurrently.  This is the opposite of {@link #sequenceFx}.
8652      * @return {Ext.Element} The Element
8653      */
8654     syncFx : function(){
8655         var me = this;
8656         me.fxDefaults = Ext.apply(me.fxDefaults || {}, {
8657             block : FALSE,
8658             concurrent : TRUE,
8659             stopFx : FALSE
8660         });
8661         return me;
8662     },
8663
8664     /**
8665      * Ensures that all effects queued after sequenceFx is called on the element are
8666      * run in sequence.  This is the opposite of {@link #syncFx}.
8667      * @return {Ext.Element} The Element
8668      */
8669     sequenceFx : function(){
8670         var me = this;
8671         me.fxDefaults = Ext.apply(me.fxDefaults || {}, {
8672             block : FALSE,
8673             concurrent : FALSE,
8674             stopFx : FALSE
8675         });
8676         return me;
8677     },
8678
8679     /* @private */
8680     nextFx : function(){        
8681         var ef = getQueue(this.dom.id)[0];
8682         if(ef){
8683             ef.call(this);
8684         }
8685     },
8686
8687     /**
8688      * Returns true if the element has any effects actively running or queued, else returns false.
8689      * @return {Boolean} True if element has active effects, else false
8690      */
8691     hasActiveFx : function(){
8692         return getQueue(this.dom.id)[0];
8693     },
8694
8695     /**
8696      * Stops any running effects and clears the element's internal effects queue if it contains
8697      * any additional effects that haven't started yet.
8698      * @return {Ext.Element} The Element
8699      */
8700     stopFx : function(finish){
8701         var me = this,
8702             id = me.dom.id;
8703         if(me.hasActiveFx()){
8704             var cur = getQueue(id)[0];
8705             if(cur && cur.anim){
8706                 if(cur.anim.isAnimated){
8707                     setQueue(id, [cur]); //clear
8708                     cur.anim.stop(finish !== undefined ? finish : TRUE);
8709                 }else{
8710                     setQueue(id, []);
8711                 }
8712             }
8713         }
8714         return me;
8715     },
8716
8717     /* @private */
8718     beforeFx : function(o){
8719         if(this.hasActiveFx() && !o.concurrent){
8720            if(o.stopFx){
8721                this.stopFx();
8722                return TRUE;
8723            }
8724            return FALSE;
8725         }
8726         return TRUE;
8727     },
8728
8729     /**
8730      * Returns true if the element is currently blocking so that no other effect can be queued
8731      * until this effect is finished, else returns false if blocking is not set.  This is commonly
8732      * used to ensure that an effect initiated by a user action runs to completion prior to the
8733      * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
8734      * @return {Boolean} True if blocking, else false
8735      */
8736     hasFxBlock : function(){
8737         var q = getQueue(this.dom.id);
8738         return q && q[0] && q[0].block;
8739     },
8740
8741     /* @private */
8742     queueFx : function(o, fn){
8743         var me = fly(this.dom);
8744         if(!me.hasFxBlock()){
8745             Ext.applyIf(o, me.fxDefaults);
8746             if(!o.concurrent){
8747                 var run = me.beforeFx(o);
8748                 fn.block = o.block;
8749                 getQueue(me.dom.id).push(fn);
8750                 if(run){
8751                     me.nextFx();
8752                 }
8753             }else{
8754                 fn.call(me);
8755             }
8756         }
8757         return me;
8758     },
8759
8760     /* @private */
8761     fxWrap : function(pos, o, vis){ 
8762         var dom = this.dom,
8763             wrap,
8764             wrapXY;
8765         if(!o.wrap || !(wrap = Ext.getDom(o.wrap))){            
8766             if(o.fixPosition){
8767                 wrapXY = fly(dom).getXY();
8768             }
8769             var div = document.createElement("div");
8770             div.style.visibility = vis;
8771             wrap = dom.parentNode.insertBefore(div, dom);
8772             fly(wrap).setPositioning(pos);
8773             if(fly(wrap).isStyle(POSITION, "static")){
8774                 fly(wrap).position("relative");
8775             }
8776             fly(dom).clearPositioning('auto');
8777             fly(wrap).clip();
8778             wrap.appendChild(dom);
8779             if(wrapXY){
8780                 fly(wrap).setXY(wrapXY);
8781             }
8782         }
8783         return wrap;
8784     },
8785
8786     /* @private */
8787     fxUnwrap : function(wrap, pos, o){      
8788         var dom = this.dom;
8789         fly(dom).clearPositioning();
8790         fly(dom).setPositioning(pos);
8791         if(!o.wrap){
8792             var pn = fly(wrap).dom.parentNode;
8793             pn.insertBefore(dom, wrap); 
8794             fly(wrap).remove();
8795         }
8796     },
8797
8798     /* @private */
8799     getFxRestore : function(){
8800         var st = this.dom.style;
8801         return {pos: this.getPositioning(), width: st.width, height : st.height};
8802     },
8803
8804     /* @private */
8805     afterFx : function(o){
8806         var dom = this.dom,
8807             id = dom.id;
8808         if(o.afterStyle){
8809             fly(dom).setStyle(o.afterStyle);            
8810         }
8811         if(o.afterCls){
8812             fly(dom).addClass(o.afterCls);
8813         }
8814         if(o.remove == TRUE){
8815             fly(dom).remove();
8816         }
8817         if(o.callback){
8818             o.callback.call(o.scope, fly(dom));
8819         }
8820         if(!o.concurrent){
8821             getQueue(id).shift();
8822             fly(dom).nextFx();
8823         }
8824     },
8825
8826     /* @private */
8827     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
8828         animType = animType || 'run';
8829         opt = opt || {};
8830         var anim = Ext.lib.Anim[animType](
8831                 this.dom, 
8832                 args,
8833                 (opt.duration || defaultDur) || .35,
8834                 (opt.easing || defaultEase) || EASEOUT,
8835                 cb,            
8836                 this
8837             );
8838         opt.anim = anim;
8839         return anim;
8840     }
8841 };
8842
8843 // backwards compat
8844 Ext.Fx.resize = Ext.Fx.scale;
8845
8846 //When included, Ext.Fx is automatically applied to Element so that all basic
8847 //effects are available directly via the Element API
8848 Ext.Element.addMethods(Ext.Fx);
8849 })();
8850 /**
8851  * @class Ext.CompositeElementLite
8852  * <p>This class encapsulates a <i>collection</i> of DOM elements, providing methods to filter
8853  * members, or to perform collective actions upon the whole set.</p>
8854  * <p>Although they are not listed, this class supports all of the methods of {@link Ext.Element} and
8855  * {@link Ext.Fx}. The methods from these classes will be performed on all the elements in this collection.</p>
8856  * Example:<pre><code>
8857 var els = Ext.select("#some-el div.some-class");
8858 // or select directly from an existing element
8859 var el = Ext.get('some-el');
8860 el.select('div.some-class');
8861
8862 els.setWidth(100); // all elements become 100 width
8863 els.hide(true); // all elements fade out and hide
8864 // or
8865 els.setWidth(100).hide(true);
8866 </code>
8867  */
8868 Ext.CompositeElementLite = function(els, root){
8869     /**
8870      * <p>The Array of DOM elements which this CompositeElement encapsulates. Read-only.</p>
8871      * <p>This will not <i>usually</i> be accessed in developers' code, but developers wishing
8872      * to augment the capabilities of the CompositeElementLite class may use it when adding
8873      * methods to the class.</p>
8874      * <p>For example to add the <code>nextAll</code> method to the class to <b>add</b> all
8875      * following siblings of selected elements, the code would be</p><code><pre>
8876 Ext.override(Ext.CompositeElementLite, {
8877     nextAll: function() {
8878         var els = this.elements, i, l = els.length, n, r = [], ri = -1;
8879
8880 //      Loop through all elements in this Composite, accumulating
8881 //      an Array of all siblings.
8882         for (i = 0; i < l; i++) {
8883             for (n = els[i].nextSibling; n; n = n.nextSibling) {
8884                 r[++ri] = n;
8885             }
8886         }
8887
8888 //      Add all found siblings to this Composite
8889         return this.add(r);
8890     }
8891 });</pre></code>
8892      * @type Array
8893      * @property elements
8894      */
8895     this.elements = [];
8896     this.add(els, root);
8897     this.el = new Ext.Element.Flyweight();
8898 };
8899
8900 Ext.CompositeElementLite.prototype = {
8901     isComposite: true,    
8902     
8903     // private
8904     getElement : function(el){
8905         // Set the shared flyweight dom property to the current element
8906         var e = this.el;
8907         e.dom = el;
8908         e.id = el.id;
8909         return e;
8910     },
8911     
8912     // private
8913     transformElement : function(el){
8914         return Ext.getDom(el);
8915     },
8916     
8917     /**
8918      * Returns the number of elements in this Composite.
8919      * @return Number
8920      */
8921     getCount : function(){
8922         return this.elements.length;
8923     },    
8924     /**
8925      * Adds elements to this Composite object.
8926      * @param {Mixed} els Either an Array of DOM elements to add, or another Composite object who's elements should be added.
8927      * @return {CompositeElement} This Composite object.
8928      */
8929     add : function(els, root){
8930         var me = this,
8931             elements = me.elements;
8932         if(!els){
8933             return this;
8934         }
8935         if(Ext.isString(els)){
8936             els = Ext.Element.selectorFunction(els, root);
8937         }else if(els.isComposite){
8938             els = els.elements;
8939         }else if(!Ext.isIterable(els)){
8940             els = [els];
8941         }
8942         
8943         for(var i = 0, len = els.length; i < len; ++i){
8944             elements.push(me.transformElement(els[i]));
8945         }
8946         return me;
8947     },
8948     
8949     invoke : function(fn, args){
8950         var me = this,
8951             els = me.elements,
8952             len = els.length, 
8953             e, 
8954             i;
8955             
8956         for(i = 0; i < len; i++) {
8957             e = els[i];
8958             if(e){
8959                 Ext.Element.prototype[fn].apply(me.getElement(e), args);
8960             }
8961         }
8962         return me;
8963     },
8964     /**
8965      * Returns a flyweight Element of the dom element object at the specified index
8966      * @param {Number} index
8967      * @return {Ext.Element}
8968      */
8969     item : function(index){
8970         var me = this,
8971             el = me.elements[index],
8972             out = null;
8973
8974         if(el){
8975             out = me.getElement(el);
8976         }
8977         return out;
8978     },
8979
8980     // fixes scope with flyweight
8981     addListener : function(eventName, handler, scope, opt){
8982         var els = this.elements,
8983             len = els.length,
8984             i, e;
8985         
8986         for(i = 0; i<len; i++) {
8987             e = els[i];
8988             if(e) {
8989                 Ext.EventManager.on(e, eventName, handler, scope || e, opt);
8990             }
8991         }
8992         return this;
8993     },
8994     /**
8995      * <p>Calls the passed function for each element in this composite.</p>
8996      * @param {Function} fn The function to call. The function is passed the following parameters:<ul>
8997      * <li><b>el</b> : Element<div class="sub-desc">The current Element in the iteration.
8998      * <b>This is the flyweight (shared) Ext.Element instance, so if you require a
8999      * a reference to the dom node, use el.dom.</b></div></li>
9000      * <li><b>c</b> : Composite<div class="sub-desc">This Composite object.</div></li>
9001      * <li><b>idx</b> : Number<div class="sub-desc">The zero-based index in the iteration.</div></li>
9002      * </ul>
9003      * @param {Object} scope (optional) The scope (<i>this</i> reference) in which the function is executed. (defaults to the Element)
9004      * @return {CompositeElement} this
9005      */
9006     each : function(fn, scope){       
9007         var me = this,
9008             els = me.elements,
9009             len = els.length,
9010             i, e;
9011         
9012         for(i = 0; i<len; i++) {
9013             e = els[i];
9014             if(e){
9015                 e = this.getElement(e);
9016                 if(fn.call(scope || e, e, me, i) === false){
9017                     break;
9018                 }
9019             }
9020         }
9021         return me;
9022     },
9023     
9024     /**
9025     * Clears this Composite and adds the elements passed.
9026     * @param {Mixed} els Either an array of DOM elements, or another Composite from which to fill this Composite.
9027     * @return {CompositeElement} this
9028     */
9029     fill : function(els){
9030         var me = this;
9031         me.elements = [];
9032         me.add(els);
9033         return me;
9034     },
9035     
9036     /**
9037      * Filters this composite to only elements that match the passed selector.
9038      * @param {String/Function} selector A string CSS selector or a comparison function.
9039      * The comparison function will be called with the following arguments:<ul>
9040      * <li><code>el</code> : Ext.Element<div class="sub-desc">The current DOM element.</div></li>
9041      * <li><code>index</code> : Number<div class="sub-desc">The current index within the collection.</div></li>
9042      * </ul>
9043      * @return {CompositeElement} this
9044      */
9045     filter : function(selector){
9046         var els = [],
9047             me = this,
9048             elements = me.elements,
9049             fn = Ext.isFunction(selector) ? selector
9050                 : function(el){
9051                     return el.is(selector);
9052                 };
9053                 
9054         
9055         me.each(function(el, self, i){
9056             if(fn(el, i) !== false){
9057                 els[els.length] = me.transformElement(el);
9058             }
9059         });
9060         me.elements = els;
9061         return me;
9062     },
9063     
9064     /**
9065      * Find the index of the passed element within the composite collection.
9066      * @param el {Mixed} The id of an element, or an Ext.Element, or an HtmlElement to find within the composite collection.
9067      * @return Number The index of the passed Ext.Element in the composite collection, or -1 if not found.
9068      */
9069     indexOf : function(el){
9070         return this.elements.indexOf(this.transformElement(el));
9071     },
9072     
9073     /**
9074     * Replaces the specified element with the passed element.
9075     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
9076     * to replace.
9077     * @param {Mixed} replacement The id of an element or the Element itself.
9078     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
9079     * @return {CompositeElement} this
9080     */    
9081     replaceElement : function(el, replacement, domReplace){
9082         var index = !isNaN(el) ? el : this.indexOf(el),
9083             d;
9084         if(index > -1){
9085             replacement = Ext.getDom(replacement);
9086             if(domReplace){
9087                 d = this.elements[index];
9088                 d.parentNode.insertBefore(replacement, d);
9089                 Ext.removeNode(d);
9090             }
9091             this.elements.splice(index, 1, replacement);
9092         }
9093         return this;
9094     },
9095     
9096     /**
9097      * Removes all elements.
9098      */
9099     clear : function(){
9100         this.elements = [];
9101     }
9102 };
9103
9104 Ext.CompositeElementLite.prototype.on = Ext.CompositeElementLite.prototype.addListener;
9105
9106 (function(){
9107 var fnName,
9108     ElProto = Ext.Element.prototype,
9109     CelProto = Ext.CompositeElementLite.prototype;
9110     
9111 for(fnName in ElProto){
9112     if(Ext.isFunction(ElProto[fnName])){
9113         (function(fnName){ 
9114             CelProto[fnName] = CelProto[fnName] || function(){
9115                 return this.invoke(fnName, arguments);
9116             };
9117         }).call(CelProto, fnName);
9118         
9119     }
9120 }
9121 })();
9122
9123 if(Ext.DomQuery){
9124     Ext.Element.selectorFunction = Ext.DomQuery.select;
9125
9126
9127 /**
9128  * Selects elements based on the passed CSS selector to enable {@link Ext.Element Element} methods
9129  * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or
9130  * {@link Ext.CompositeElementLite CompositeElementLite} object.
9131  * @param {String/Array} selector The CSS selector or an array of elements
9132  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9133  * @return {CompositeElementLite/CompositeElement}
9134  * @member Ext.Element
9135  * @method select
9136  */
9137 Ext.Element.select = function(selector, root){
9138     var els;
9139     if(typeof selector == "string"){
9140         els = Ext.Element.selectorFunction(selector, root);
9141     }else if(selector.length !== undefined){
9142         els = selector;
9143     }else{
9144         throw "Invalid selector";
9145     }
9146     return new Ext.CompositeElementLite(els);
9147 };
9148 /**
9149  * Selects elements based on the passed CSS selector to enable {@link Ext.Element Element} methods
9150  * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or
9151  * {@link Ext.CompositeElementLite CompositeElementLite} object.
9152  * @param {String/Array} selector The CSS selector or an array of elements
9153  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9154  * @return {CompositeElementLite/CompositeElement}
9155  * @member Ext
9156  * @method select
9157  */
9158 Ext.select = Ext.Element.select;/**
9159  * @class Ext.CompositeElementLite
9160  */
9161 Ext.apply(Ext.CompositeElementLite.prototype, { 
9162         addElements : function(els, root){
9163         if(!els){
9164             return this;
9165         }
9166         if(typeof els == "string"){
9167             els = Ext.Element.selectorFunction(els, root);
9168         }
9169         var yels = this.elements;        
9170             Ext.each(els, function(e) {
9171                 yels.push(Ext.get(e));
9172         });
9173         return this;
9174     },
9175     
9176     /**
9177      * Returns the first Element
9178      * @return {Ext.Element}
9179      */
9180     first : function(){
9181         return this.item(0);
9182     },   
9183     
9184     /**
9185      * Returns the last Element
9186      * @return {Ext.Element}
9187      */
9188     last : function(){
9189         return this.item(this.getCount()-1);
9190     },
9191     
9192     /**
9193      * Returns true if this composite contains the passed element
9194      * @param el {Mixed} The id of an element, or an Ext.Element, or an HtmlElement to find within the composite collection.
9195      * @return Boolean
9196      */
9197     contains : function(el){
9198         return this.indexOf(el) != -1;
9199     },
9200     
9201     /**
9202     * Removes the specified element(s).
9203     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
9204     * or an array of any of those.
9205     * @param {Boolean} removeDom (optional) True to also remove the element from the document
9206     * @return {CompositeElement} this
9207     */
9208     removeElement : function(keys, removeDom){
9209         var me = this,
9210                 els = this.elements,        
9211                 el;             
9212             Ext.each(keys, function(val){
9213                     if ((el = (els[val] || els[val = me.indexOf(val)]))) {
9214                         if(removeDom){
9215                     if(el.dom){
9216                         el.remove();
9217                     }else{
9218                         Ext.removeNode(el);
9219                     }
9220                 }
9221                         els.splice(val, 1);                     
9222                         }
9223             });
9224         return this;
9225     }    
9226 });
9227 /**
9228  * @class Ext.CompositeElement
9229  * @extends Ext.CompositeElementLite
9230  * <p>This class encapsulates a <i>collection</i> of DOM elements, providing methods to filter
9231  * members, or to perform collective actions upon the whole set.</p>
9232  * <p>Although they are not listed, this class supports all of the methods of {@link Ext.Element} and
9233  * {@link Ext.Fx}. The methods from these classes will be performed on all the elements in this collection.</p>
9234  * <p>All methods return <i>this</i> and can be chained.</p>
9235  * Usage:
9236 <pre><code>
9237 var els = Ext.select("#some-el div.some-class", true);
9238 // or select directly from an existing element
9239 var el = Ext.get('some-el');
9240 el.select('div.some-class', true);
9241
9242 els.setWidth(100); // all elements become 100 width
9243 els.hide(true); // all elements fade out and hide
9244 // or
9245 els.setWidth(100).hide(true);
9246 </code></pre>
9247  */
9248 Ext.CompositeElement = function(els, root){
9249     this.elements = [];
9250     this.add(els, root);
9251 };
9252
9253 Ext.extend(Ext.CompositeElement, Ext.CompositeElementLite, {
9254     
9255     // private
9256     getElement : function(el){
9257         // In this case just return it, since we already have a reference to it
9258         return el;
9259     },
9260     
9261     // private
9262     transformElement : function(el){
9263         return Ext.get(el);
9264     }
9265
9266     /**
9267     * Adds elements to this composite.
9268     * @param {String/Array} els A string CSS selector, an array of elements or an element
9269     * @return {CompositeElement} this
9270     */
9271
9272     /**
9273      * Returns the Element object at the specified index
9274      * @param {Number} index
9275      * @return {Ext.Element}
9276      */
9277
9278     /**
9279      * Iterates each <code>element</code> in this <code>composite</code>
9280      * calling the supplied function using {@link Ext#each}.
9281      * @param {Function} fn The function to be called with each
9282      * <code>element</code>. If the supplied function returns <tt>false</tt>,
9283      * iteration stops. This function is called with the following arguments:
9284      * <div class="mdetail-params"><ul>
9285      * <li><code>element</code> : <i>Ext.Element</i><div class="sub-desc">The element at the current <code>index</code>
9286      * in the <code>composite</code></div></li>
9287      * <li><code>composite</code> : <i>Object</i> <div class="sub-desc">This composite.</div></li>
9288      * <li><code>index</code> : <i>Number</i> <div class="sub-desc">The current index within the <code>composite</code> </div></li>
9289      * </ul></div>
9290      * @param {Object} scope (optional) The scope (<code><this</code> reference) in which the specified function is executed.
9291      * Defaults to the <code>element</code> at the current <code>index</code>
9292      * within the composite.
9293      * @return {CompositeElement} this
9294      */
9295 });
9296
9297 /**
9298  * Selects elements based on the passed CSS selector to enable {@link Ext.Element Element} methods
9299  * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or
9300  * {@link Ext.CompositeElementLite CompositeElementLite} object.
9301  * @param {String/Array} selector The CSS selector or an array of elements
9302  * @param {Boolean} unique (optional) true to create a unique Ext.Element for each element (defaults to a shared flyweight object)
9303  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9304  * @return {CompositeElementLite/CompositeElement}
9305  * @member Ext.Element
9306  * @method select
9307  */
9308 Ext.Element.select = function(selector, unique, root){
9309     var els;
9310     if(typeof selector == "string"){
9311         els = Ext.Element.selectorFunction(selector, root);
9312     }else if(selector.length !== undefined){
9313         els = selector;
9314     }else{
9315         throw "Invalid selector";
9316     }
9317
9318     return (unique === true) ? new Ext.CompositeElement(els) : new Ext.CompositeElementLite(els);
9319 };
9320
9321 /**
9322  * Selects elements based on the passed CSS selector to enable {@link Ext.Element Element} methods
9323  * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or
9324  * {@link Ext.CompositeElementLite CompositeElementLite} object.
9325  * @param {String/Array} selector The CSS selector or an array of elements
9326  * @param {Boolean} unique (optional) true to create a unique Ext.Element for each element (defaults to a shared flyweight object)
9327  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9328  * @return {CompositeElementLite/CompositeElement}
9329  * @member Ext.Element
9330  * @method select
9331  */
9332 Ext.select = Ext.Element.select;(function(){
9333     var BEFOREREQUEST = "beforerequest",
9334         REQUESTCOMPLETE = "requestcomplete",
9335         REQUESTEXCEPTION = "requestexception",
9336         UNDEFINED = undefined,
9337         LOAD = 'load',
9338         POST = 'POST',
9339         GET = 'GET',
9340         WINDOW = window;
9341
9342     /**
9343      * @class Ext.data.Connection
9344      * @extends Ext.util.Observable
9345      * <p>The class encapsulates a connection to the page's originating domain, allowing requests to be made
9346      * either to a configured URL, or to a URL specified at request time.</p>
9347      * <p>Requests made by this class are asynchronous, and will return immediately. No data from
9348      * the server will be available to the statement immediately following the {@link #request} call.
9349      * To process returned data, use a
9350      * <a href="#request-option-success" ext:member="request-option-success" ext:cls="Ext.data.Connection">success callback</a>
9351      * in the request options object,
9352      * or an {@link #requestcomplete event listener}.</p>
9353      * <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
9354      * is they are <b>not</b> performed using XMLHttpRequests. Instead the form is submitted in the standard
9355      * manner with the DOM <tt>&lt;form></tt> element temporarily modified to have its
9356      * <a href="http://www.w3.org/TR/REC-html40/present/frames.html#adef-target">target</a> set to refer
9357      * to a dynamically generated, hidden <tt>&lt;iframe></tt> which is inserted into the document
9358      * but removed after the return data has been gathered.</p>
9359      * <p>The server response is parsed by the browser to create the document for the IFRAME. If the
9360      * server is using JSON to send the return object, then the
9361      * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.17">Content-Type</a> header
9362      * must be set to "text/html" in order to tell the browser to insert the text unchanged into the document body.</p>
9363      * <p>Characters which are significant to an HTML parser must be sent as HTML entities, so encode
9364      * "&lt;" as "&amp;lt;", "&amp;" as "&amp;amp;" etc.</p>
9365      * <p>The response text is retrieved from the document, and a fake XMLHttpRequest object
9366      * is created containing a <tt>responseText</tt> property in order to conform to the
9367      * requirements of event handlers and callbacks.</p>
9368      * <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>
9369      * and some server technologies (notably JEE) may require some custom processing in order to
9370      * retrieve parameter names and parameter values from the packet content.</p>
9371      * @constructor
9372      * @param {Object} config a configuration object.
9373      */
9374     Ext.data.Connection = function(config){
9375         Ext.apply(this, config);
9376         this.addEvents(
9377             /**
9378              * @event beforerequest
9379              * Fires before a network request is made to retrieve a data object.
9380              * @param {Connection} conn This Connection object.
9381              * @param {Object} options The options config object passed to the {@link #request} method.
9382              */
9383             BEFOREREQUEST,
9384             /**
9385              * @event requestcomplete
9386              * Fires if the request was successfully completed.
9387              * @param {Connection} conn This Connection object.
9388              * @param {Object} response The XHR object containing the response data.
9389              * See <a href="http://www.w3.org/TR/XMLHttpRequest/">The XMLHttpRequest Object</a>
9390              * for details.
9391              * @param {Object} options The options config object passed to the {@link #request} method.
9392              */
9393             REQUESTCOMPLETE,
9394             /**
9395              * @event requestexception
9396              * Fires if an error HTTP status was returned from the server.
9397              * See <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html">HTTP Status Code Definitions</a>
9398              * for details of HTTP status codes.
9399              * @param {Connection} conn This Connection object.
9400              * @param {Object} response The XHR object containing the response data.
9401              * See <a href="http://www.w3.org/TR/XMLHttpRequest/">The XMLHttpRequest Object</a>
9402              * for details.
9403              * @param {Object} options The options config object passed to the {@link #request} method.
9404              */
9405             REQUESTEXCEPTION
9406         );
9407         Ext.data.Connection.superclass.constructor.call(this);
9408     };
9409
9410     Ext.extend(Ext.data.Connection, Ext.util.Observable, {
9411         /**
9412          * @cfg {String} url (Optional) <p>The default URL to be used for requests to the server. Defaults to undefined.</p>
9413          * <p>The <code>url</code> config may be a function which <i>returns</i> the URL to use for the Ajax request. The scope
9414          * (<code><b>this</b></code> reference) of the function is the <code>scope</code> option passed to the {@link #request} method.</p>
9415          */
9416         /**
9417          * @cfg {Object} extraParams (Optional) An object containing properties which are used as
9418          * extra parameters to each request made by this object. (defaults to undefined)
9419          */
9420         /**
9421          * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
9422          *  to each request made by this object. (defaults to undefined)
9423          */
9424         /**
9425          * @cfg {String} method (Optional) The default HTTP method to be used for requests.
9426          * (defaults to undefined; if not set, but {@link #request} params are present, POST will be used;
9427          * otherwise, GET will be used.)
9428          */
9429         /**
9430          * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
9431          */
9432         timeout : 30000,
9433         /**
9434          * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
9435          * @type Boolean
9436          */
9437         autoAbort:false,
9438
9439         /**
9440          * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
9441          * @type Boolean
9442          */
9443         disableCaching: true,
9444
9445         /**
9446          * @cfg {String} disableCachingParam (Optional) Change the parameter which is sent went disabling caching
9447          * through a cache buster. Defaults to '_dc'
9448          * @type String
9449          */
9450         disableCachingParam: '_dc',
9451
9452         /**
9453          * <p>Sends an HTTP request to a remote server.</p>
9454          * <p><b>Important:</b> Ajax server requests are asynchronous, and this call will
9455          * return before the response has been received. Process any returned data
9456          * in a callback function.</p>
9457          * <pre><code>
9458 Ext.Ajax.request({
9459    url: 'ajax_demo/sample.json',
9460    success: function(response, opts) {
9461       var obj = Ext.decode(response.responseText);
9462       console.dir(obj);
9463    },
9464    failure: function(response, opts) {
9465       console.log('server-side failure with status code ' + response.status);
9466    }
9467 });
9468          * </code></pre>
9469          * <p>To execute a callback function in the correct scope, use the <tt>scope</tt> option.</p>
9470          * @param {Object} options An object which may contain the following properties:<ul>
9471          * <li><b>url</b> : String/Function (Optional)<div class="sub-desc">The URL to
9472          * which to send the request, or a function to call which returns a URL string. The scope of the
9473          * function is specified by the <tt>scope</tt> option. Defaults to the configured
9474          * <tt>{@link #url}</tt>.</div></li>
9475          * <li><b>params</b> : Object/String/Function (Optional)<div class="sub-desc">
9476          * An object containing properties which are used as parameters to the
9477          * request, a url encoded string or a function to call to get either. The scope of the function
9478          * is specified by the <tt>scope</tt> option.</div></li>
9479          * <li><b>method</b> : String (Optional)<div class="sub-desc">The HTTP method to use
9480          * for the request. Defaults to the configured method, or if no method was configured,
9481          * "GET" if no parameters are being sent, and "POST" if parameters are being sent.  Note that
9482          * the method name is case-sensitive and should be all caps.</div></li>
9483          * <li><b>callback</b> : Function (Optional)<div class="sub-desc">The
9484          * function to be called upon receipt of the HTTP response. The callback is
9485          * called regardless of success or failure and is passed the following
9486          * parameters:<ul>
9487          * <li><b>options</b> : Object<div class="sub-desc">The parameter to the request call.</div></li>
9488          * <li><b>success</b> : Boolean<div class="sub-desc">True if the request succeeded.</div></li>
9489          * <li><b>response</b> : Object<div class="sub-desc">The XMLHttpRequest object containing the response data.
9490          * See <a href="http://www.w3.org/TR/XMLHttpRequest/">http://www.w3.org/TR/XMLHttpRequest/</a> for details about
9491          * accessing elements of the response.</div></li>
9492          * </ul></div></li>
9493          * <li><a id="request-option-success"></a><b>success</b> : Function (Optional)<div class="sub-desc">The function
9494          * to be called upon success of the request. The callback is passed the following
9495          * parameters:<ul>
9496          * <li><b>response</b> : Object<div class="sub-desc">The XMLHttpRequest object containing the response data.</div></li>
9497          * <li><b>options</b> : Object<div class="sub-desc">The parameter to the request call.</div></li>
9498          * </ul></div></li>
9499          * <li><b>failure</b> : Function (Optional)<div class="sub-desc">The function
9500          * to be called upon failure of the request. The callback is passed the
9501          * following parameters:<ul>
9502          * <li><b>response</b> : Object<div class="sub-desc">The XMLHttpRequest object containing the response data.</div></li>
9503          * <li><b>options</b> : Object<div class="sub-desc">The parameter to the request call.</div></li>
9504          * </ul></div></li>
9505          * <li><b>scope</b> : Object (Optional)<div class="sub-desc">The scope in
9506          * which to execute the callbacks: The "this" object for the callback function. If the <tt>url</tt>, or <tt>params</tt> options were
9507          * specified as functions from which to draw values, then this also serves as the scope for those function calls.
9508          * Defaults to the browser window.</div></li>
9509          * <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>
9510          * <li><b>form</b> : Element/HTMLElement/String (Optional)<div class="sub-desc">The <tt>&lt;form&gt;</tt>
9511          * Element or the id of the <tt>&lt;form&gt;</tt> to pull parameters from.</div></li>
9512          * <li><a id="request-option-isUpload"></a><b>isUpload</b> : Boolean (Optional)<div class="sub-desc"><b>Only meaningful when used
9513          * with the <tt>form</tt> option</b>.
9514          * <p>True if the form object is a file upload (will be set automatically if the form was
9515          * configured with <b><tt>enctype</tt></b> "multipart/form-data").</p>
9516          * <p>File uploads are not performed using normal "Ajax" techniques, that is they are <b>not</b>
9517          * performed using XMLHttpRequests. Instead the form is submitted in the standard manner with the
9518          * DOM <tt>&lt;form></tt> element temporarily modified to have its
9519          * <a href="http://www.w3.org/TR/REC-html40/present/frames.html#adef-target">target</a> set to refer
9520          * to a dynamically generated, hidden <tt>&lt;iframe></tt> which is inserted into the document
9521          * but removed after the return data has been gathered.</p>
9522          * <p>The server response is parsed by the browser to create the document for the IFRAME. If the
9523          * server is using JSON to send the return object, then the
9524          * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.17">Content-Type</a> header
9525          * must be set to "text/html" in order to tell the browser to insert the text unchanged into the document body.</p>
9526          * <p>The response text is retrieved from the document, and a fake XMLHttpRequest object
9527          * is created containing a <tt>responseText</tt> property in order to conform to the
9528          * requirements of event handlers and callbacks.</p>
9529          * <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>
9530          * and some server technologies (notably JEE) may require some custom processing in order to
9531          * retrieve parameter names and parameter values from the packet content.</p>
9532          * </div></li>
9533          * <li><b>headers</b> : Object (Optional)<div class="sub-desc">Request
9534          * headers to set for the request.</div></li>
9535          * <li><b>xmlData</b> : Object (Optional)<div class="sub-desc">XML document
9536          * to use for the post. Note: This will be used instead of params for the post
9537          * data. Any params will be appended to the URL.</div></li>
9538          * <li><b>jsonData</b> : Object/String (Optional)<div class="sub-desc">JSON
9539          * data to use as the post. Note: This will be used instead of params for the post
9540          * data. Any params will be appended to the URL.</div></li>
9541          * <li><b>disableCaching</b> : Boolean (Optional)<div class="sub-desc">True
9542          * to add a unique cache-buster param to GET requests.</div></li>
9543          * </ul></p>
9544          * <p>The options object may also contain any other property which might be needed to perform
9545          * postprocessing in a callback because it is passed to callback functions.</p>
9546          * @return {Number} transactionId The id of the server transaction. This may be used
9547          * to cancel the request.
9548          */
9549         request : function(o){
9550             var me = this;
9551             if(me.fireEvent(BEFOREREQUEST, me, o)){
9552                 if (o.el) {
9553                     if(!Ext.isEmpty(o.indicatorText)){
9554                         me.indicatorText = '<div class="loading-indicator">'+o.indicatorText+"</div>";
9555                     }
9556                     if(me.indicatorText) {
9557                         Ext.getDom(o.el).innerHTML = me.indicatorText;
9558                     }
9559                     o.success = (Ext.isFunction(o.success) ? o.success : function(){}).createInterceptor(function(response) {
9560                         Ext.getDom(o.el).innerHTML = response.responseText;
9561                     });
9562                 }
9563
9564                 var p = o.params,
9565                     url = o.url || me.url,
9566                     method,
9567                     cb = {success: me.handleResponse,
9568                           failure: me.handleFailure,
9569                           scope: me,
9570                           argument: {options: o},
9571                           timeout : o.timeout || me.timeout
9572                     },
9573                     form,
9574                     serForm;
9575
9576
9577                 if (Ext.isFunction(p)) {
9578                     p = p.call(o.scope||WINDOW, o);
9579                 }
9580
9581                 p = Ext.urlEncode(me.extraParams, Ext.isObject(p) ? Ext.urlEncode(p) : p);
9582
9583                 if (Ext.isFunction(url)) {
9584                     url = url.call(o.scope || WINDOW, o);
9585                 }
9586
9587                 if((form = Ext.getDom(o.form))){
9588                     url = url || form.action;
9589                      if(o.isUpload || /multipart\/form-data/i.test(form.getAttribute("enctype"))) {
9590                          return me.doFormUpload.call(me, o, p, url);
9591                      }
9592                     serForm = Ext.lib.Ajax.serializeForm(form);
9593                     p = p ? (p + '&' + serForm) : serForm;
9594                 }
9595
9596                 method = o.method || me.method || ((p || o.xmlData || o.jsonData) ? POST : GET);
9597
9598                 if(method === GET && (me.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
9599                     var dcp = o.disableCachingParam || me.disableCachingParam;
9600                     url = Ext.urlAppend(url, dcp + '=' + (new Date().getTime()));
9601                 }
9602
9603                 o.headers = Ext.apply(o.headers || {}, me.defaultHeaders || {});
9604
9605                 if(o.autoAbort === true || me.autoAbort) {
9606                     me.abort();
9607                 }
9608
9609                 if((method == GET || o.xmlData || o.jsonData) && p){
9610                     url = Ext.urlAppend(url, p);
9611                     p = '';
9612                 }
9613                 return (me.transId = Ext.lib.Ajax.request(method, url, cb, p, o));
9614             }else{
9615                 return o.callback ? o.callback.apply(o.scope, [o,UNDEFINED,UNDEFINED]) : null;
9616             }
9617         },
9618
9619         /**
9620          * Determine whether this object has a request outstanding.
9621          * @param {Number} transactionId (Optional) defaults to the last transaction
9622          * @return {Boolean} True if there is an outstanding request.
9623          */
9624         isLoading : function(transId){
9625             return transId ? Ext.lib.Ajax.isCallInProgress(transId) : !! this.transId;
9626         },
9627
9628         /**
9629          * Aborts any outstanding request.
9630          * @param {Number} transactionId (Optional) defaults to the last transaction
9631          */
9632         abort : function(transId){
9633             if(transId || this.isLoading()){
9634                 Ext.lib.Ajax.abort(transId || this.transId);
9635             }
9636         },
9637
9638         // private
9639         handleResponse : function(response){
9640             this.transId = false;
9641             var options = response.argument.options;
9642             response.argument = options ? options.argument : null;
9643             this.fireEvent(REQUESTCOMPLETE, this, response, options);
9644             if(options.success){
9645                 options.success.call(options.scope, response, options);
9646             }
9647             if(options.callback){
9648                 options.callback.call(options.scope, options, true, response);
9649             }
9650         },
9651
9652         // private
9653         handleFailure : function(response, e){
9654             this.transId = false;
9655             var options = response.argument.options;
9656             response.argument = options ? options.argument : null;
9657             this.fireEvent(REQUESTEXCEPTION, this, response, options, e);
9658             if(options.failure){
9659                 options.failure.call(options.scope, response, options);
9660             }
9661             if(options.callback){
9662                 options.callback.call(options.scope, options, false, response);
9663             }
9664         },
9665
9666         // private
9667         doFormUpload : function(o, ps, url){
9668             var id = Ext.id(),
9669                 doc = document,
9670                 frame = doc.createElement('iframe'),
9671                 form = Ext.getDom(o.form),
9672                 hiddens = [],
9673                 hd,
9674                 encoding = 'multipart/form-data',
9675                 buf = {
9676                     target: form.target,
9677                     method: form.method,
9678                     encoding: form.encoding,
9679                     enctype: form.enctype,
9680                     action: form.action
9681                 };
9682
9683             Ext.fly(frame).set({
9684                 id: id,
9685                 name: id,
9686                 cls: 'x-hidden'
9687
9688             });
9689
9690             doc.body.appendChild(frame);
9691
9692             //Reset the Frame to neutral domain
9693             Ext.fly(frame).set({
9694                src : Ext.SSL_SECURE_URL
9695             });
9696
9697             // This is required so that IE doesn't pop the response up in a new window.
9698             if(Ext.isIE){
9699                document.frames[id].name = id;
9700             }
9701
9702
9703             Ext.fly(form).set({
9704                 target: id,
9705                 method: POST,
9706                 enctype: encoding,
9707                 encoding: encoding,
9708                 action: url || buf.action
9709             });
9710
9711             // add dynamic params
9712             Ext.iterate(Ext.urlDecode(ps, false), function(k, v){
9713                 hd = doc.createElement('input');
9714                 Ext.fly(hd).set({
9715                     type: 'hidden',
9716                     value: v,
9717                     name: k
9718                 });
9719                 form.appendChild(hd);
9720                 hiddens.push(hd);
9721             });
9722
9723             function cb(){
9724                 var me = this,
9725                     // bogus response object
9726                     r = {responseText : '',
9727                          responseXML : null,
9728                          argument : o.argument},
9729                     doc,
9730                     firstChild;
9731
9732                 try{
9733                     doc = frame.contentWindow.document || frame.contentDocument || WINDOW.frames[id].document;
9734                     if(doc){
9735                         if(doc.body){
9736                             if(/textarea/i.test((firstChild = doc.body.firstChild || {}).tagName)){ // json response wrapped in textarea
9737                                 r.responseText = firstChild.value;
9738                             }else{
9739                                 r.responseText = doc.body.innerHTML;
9740                             }
9741                         }
9742                         //in IE the document may still have a body even if returns XML.
9743                         r.responseXML = doc.XMLDocument || doc;
9744                     }
9745                 }
9746                 catch(e) {}
9747
9748                 Ext.EventManager.removeListener(frame, LOAD, cb, me);
9749
9750                 me.fireEvent(REQUESTCOMPLETE, me, r, o);
9751
9752                 function runCallback(fn, scope, args){
9753                     if(Ext.isFunction(fn)){
9754                         fn.apply(scope, args);
9755                     }
9756                 }
9757
9758                 runCallback(o.success, o.scope, [r, o]);
9759                 runCallback(o.callback, o.scope, [o, true, r]);
9760
9761                 if(!me.debugUploads){
9762                     setTimeout(function(){Ext.removeNode(frame);}, 100);
9763                 }
9764             }
9765
9766             Ext.EventManager.on(frame, LOAD, cb, this);
9767             form.submit();
9768
9769             Ext.fly(form).set(buf);
9770             Ext.each(hiddens, function(h) {
9771                 Ext.removeNode(h);
9772             });
9773         }
9774     });
9775 })();
9776
9777 /**
9778  * @class Ext.Ajax
9779  * @extends Ext.data.Connection
9780  * <p>The global Ajax request class that provides a simple way to make Ajax requests
9781  * with maximum flexibility.</p>
9782  * <p>Since Ext.Ajax is a singleton, you can set common properties/events for it once
9783  * and override them at the request function level only if necessary.</p>
9784  * <p>Common <b>Properties</b> you may want to set are:<div class="mdetail-params"><ul>
9785  * <li><b><tt>{@link #method}</tt></b><p class="sub-desc"></p></li>
9786  * <li><b><tt>{@link #extraParams}</tt></b><p class="sub-desc"></p></li>
9787  * <li><b><tt>{@link #url}</tt></b><p class="sub-desc"></p></li>
9788  * </ul></div>
9789  * <pre><code>
9790 // Default headers to pass in every request
9791 Ext.Ajax.defaultHeaders = {
9792     'Powered-By': 'Ext'
9793 };
9794  * </code></pre>
9795  * </p>
9796  * <p>Common <b>Events</b> you may want to set are:<div class="mdetail-params"><ul>
9797  * <li><b><tt>{@link Ext.data.Connection#beforerequest beforerequest}</tt></b><p class="sub-desc"></p></li>
9798  * <li><b><tt>{@link Ext.data.Connection#requestcomplete requestcomplete}</tt></b><p class="sub-desc"></p></li>
9799  * <li><b><tt>{@link Ext.data.Connection#requestexception requestexception}</tt></b><p class="sub-desc"></p></li>
9800  * </ul></div>
9801  * <pre><code>
9802 // Example: show a spinner during all Ajax requests
9803 Ext.Ajax.on('beforerequest', this.showSpinner, this);
9804 Ext.Ajax.on('requestcomplete', this.hideSpinner, this);
9805 Ext.Ajax.on('requestexception', this.hideSpinner, this);
9806  * </code></pre>
9807  * </p>
9808  * <p>An example request:</p>
9809  * <pre><code>
9810 // Basic request
9811 Ext.Ajax.{@link Ext.data.Connection#request request}({
9812    url: 'foo.php',
9813    success: someFn,
9814    failure: otherFn,
9815    headers: {
9816        'my-header': 'foo'
9817    },
9818    params: { foo: 'bar' }
9819 });
9820
9821 // Simple ajax form submission
9822 Ext.Ajax.{@link Ext.data.Connection#request request}({
9823     form: 'some-form',
9824     params: 'foo=bar'
9825 });
9826  * </code></pre>
9827  * </p>
9828  * @singleton
9829  */
9830 Ext.Ajax = new Ext.data.Connection({
9831     /**
9832      * @cfg {String} url @hide
9833      */
9834     /**
9835      * @cfg {Object} extraParams @hide
9836      */
9837     /**
9838      * @cfg {Object} defaultHeaders @hide
9839      */
9840     /**
9841      * @cfg {String} method (Optional) @hide
9842      */
9843     /**
9844      * @cfg {Number} timeout (Optional) @hide
9845      */
9846     /**
9847      * @cfg {Boolean} autoAbort (Optional) @hide
9848      */
9849
9850     /**
9851      * @cfg {Boolean} disableCaching (Optional) @hide
9852      */
9853
9854     /**
9855      * @property  disableCaching
9856      * True to add a unique cache-buster param to GET requests. (defaults to true)
9857      * @type Boolean
9858      */
9859     /**
9860      * @property  url
9861      * The default URL to be used for requests to the server. (defaults to undefined)
9862      * If the server receives all requests through one URL, setting this once is easier than
9863      * entering it on every request.
9864      * @type String
9865      */
9866     /**
9867      * @property  extraParams
9868      * An object containing properties which are used as extra parameters to each request made
9869      * by this object (defaults to undefined). Session information and other data that you need
9870      * to pass with each request are commonly put here.
9871      * @type Object
9872      */
9873     /**
9874      * @property  defaultHeaders
9875      * An object containing request headers which are added to each request made by this object
9876      * (defaults to undefined).
9877      * @type Object
9878      */
9879     /**
9880      * @property  method
9881      * The default HTTP method to be used for requests. Note that this is case-sensitive and
9882      * should be all caps (defaults to undefined; if not set but params are present will use
9883      * <tt>"POST"</tt>, otherwise will use <tt>"GET"</tt>.)
9884      * @type String
9885      */
9886     /**
9887      * @property  timeout
9888      * The timeout in milliseconds to be used for requests. (defaults to 30000)
9889      * @type Number
9890      */
9891
9892     /**
9893      * @property  autoAbort
9894      * Whether a new request should abort any pending requests. (defaults to false)
9895      * @type Boolean
9896      */
9897     autoAbort : false,
9898
9899     /**
9900      * Serialize the passed form into a url encoded string
9901      * @param {String/HTMLElement} form
9902      * @return {String}
9903      */
9904     serializeForm : function(form){
9905         return Ext.lib.Ajax.serializeForm(form);
9906     }
9907 });
9908 /**
9909  * @class Ext.Updater
9910  * @extends Ext.util.Observable
9911  * Provides AJAX-style update capabilities for Element objects.  Updater can be used to {@link #update}
9912  * an {@link Ext.Element} once, or you can use {@link #startAutoRefresh} to set up an auto-updating
9913  * {@link Ext.Element Element} on a specific interval.<br><br>
9914  * Usage:<br>
9915  * <pre><code>
9916  * var el = Ext.get("foo"); // Get Ext.Element object
9917  * var mgr = el.getUpdater();
9918  * mgr.update({
9919         url: "http://myserver.com/index.php",
9920         params: {
9921             param1: "foo",
9922             param2: "bar"
9923         }
9924  * });
9925  * ...
9926  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
9927  * <br>
9928  * // or directly (returns the same Updater instance)
9929  * var mgr = new Ext.Updater("myElementId");
9930  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
9931  * mgr.on("update", myFcnNeedsToKnow);
9932  * <br>
9933  * // short handed call directly from the element object
9934  * Ext.get("foo").load({
9935         url: "bar.php",
9936         scripts: true,
9937         params: "param1=foo&amp;param2=bar",
9938         text: "Loading Foo..."
9939  * });
9940  * </code></pre>
9941  * @constructor
9942  * Create new Updater directly.
9943  * @param {Mixed} el The element to update
9944  * @param {Boolean} forceNew (optional) By default the constructor checks to see if the passed element already
9945  * has an Updater and if it does it returns the same instance. This will skip that check (useful for extending this class).
9946  */
9947 Ext.UpdateManager = Ext.Updater = Ext.extend(Ext.util.Observable,
9948 function() {
9949     var BEFOREUPDATE = "beforeupdate",
9950         UPDATE = "update",
9951         FAILURE = "failure";
9952
9953     // private
9954     function processSuccess(response){
9955         var me = this;
9956         me.transaction = null;
9957         if (response.argument.form && response.argument.reset) {
9958             try { // put in try/catch since some older FF releases had problems with this
9959                 response.argument.form.reset();
9960             } catch(e){}
9961         }
9962         if (me.loadScripts) {
9963             me.renderer.render(me.el, response, me,
9964                updateComplete.createDelegate(me, [response]));
9965         } else {
9966             me.renderer.render(me.el, response, me);
9967             updateComplete.call(me, response);
9968         }
9969     }
9970
9971     // private
9972     function updateComplete(response, type, success){
9973         this.fireEvent(type || UPDATE, this.el, response);
9974         if(Ext.isFunction(response.argument.callback)){
9975             response.argument.callback.call(response.argument.scope, this.el, Ext.isEmpty(success) ? true : false, response, response.argument.options);
9976         }
9977     }
9978
9979     // private
9980     function processFailure(response){
9981         updateComplete.call(this, response, FAILURE, !!(this.transaction = null));
9982     }
9983
9984     return {
9985         constructor: function(el, forceNew){
9986             var me = this;
9987             el = Ext.get(el);
9988             if(!forceNew && el.updateManager){
9989                 return el.updateManager;
9990             }
9991             /**
9992              * The Element object
9993              * @type Ext.Element
9994              */
9995             me.el = el;
9996             /**
9997              * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
9998              * @type String
9999              */
10000             me.defaultUrl = null;
10001
10002             me.addEvents(
10003                 /**
10004                  * @event beforeupdate
10005                  * Fired before an update is made, return false from your handler and the update is cancelled.
10006                  * @param {Ext.Element} el
10007                  * @param {String/Object/Function} url
10008                  * @param {String/Object} params
10009                  */
10010                 BEFOREUPDATE,
10011                 /**
10012                  * @event update
10013                  * Fired after successful update is made.
10014                  * @param {Ext.Element} el
10015                  * @param {Object} oResponseObject The response Object
10016                  */
10017                 UPDATE,
10018                 /**
10019                  * @event failure
10020                  * Fired on update failure.
10021                  * @param {Ext.Element} el
10022                  * @param {Object} oResponseObject The response Object
10023                  */
10024                 FAILURE
10025             );
10026
10027             Ext.apply(me, Ext.Updater.defaults);
10028             /**
10029              * Blank page URL to use with SSL file uploads (defaults to {@link Ext.Updater.defaults#sslBlankUrl}).
10030              * @property sslBlankUrl
10031              * @type String
10032              */
10033             /**
10034              * Whether to append unique parameter on get request to disable caching (defaults to {@link Ext.Updater.defaults#disableCaching}).
10035              * @property disableCaching
10036              * @type Boolean
10037              */
10038             /**
10039              * Text for loading indicator (defaults to {@link Ext.Updater.defaults#indicatorText}).
10040              * @property indicatorText
10041              * @type String
10042              */
10043             /**
10044              * Whether to show indicatorText when loading (defaults to {@link Ext.Updater.defaults#showLoadIndicator}).
10045              * @property showLoadIndicator
10046              * @type String
10047              */
10048             /**
10049              * Timeout for requests or form posts in seconds (defaults to {@link Ext.Updater.defaults#timeout}).
10050              * @property timeout
10051              * @type Number
10052              */
10053             /**
10054              * True to process scripts in the output (defaults to {@link Ext.Updater.defaults#loadScripts}).
10055              * @property loadScripts
10056              * @type Boolean
10057              */
10058
10059             /**
10060              * Transaction object of the current executing transaction, or null if there is no active transaction.
10061              */
10062             me.transaction = null;
10063             /**
10064              * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
10065              * @type Function
10066              */
10067             me.refreshDelegate = me.refresh.createDelegate(me);
10068             /**
10069              * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
10070              * @type Function
10071              */
10072             me.updateDelegate = me.update.createDelegate(me);
10073             /**
10074              * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
10075              * @type Function
10076              */
10077             me.formUpdateDelegate = (me.formUpdate || function(){}).createDelegate(me);
10078
10079             /**
10080              * The renderer for this Updater (defaults to {@link Ext.Updater.BasicRenderer}).
10081              */
10082             me.renderer = me.renderer || me.getDefaultRenderer();
10083
10084             Ext.Updater.superclass.constructor.call(me);
10085         },
10086
10087         /**
10088          * Sets the content renderer for this Updater. See {@link Ext.Updater.BasicRenderer#render} for more details.
10089          * @param {Object} renderer The object implementing the render() method
10090          */
10091         setRenderer : function(renderer){
10092             this.renderer = renderer;
10093         },
10094
10095         /**
10096          * Returns the current content renderer for this Updater. See {@link Ext.Updater.BasicRenderer#render} for more details.
10097          * @return {Object}
10098          */
10099         getRenderer : function(){
10100            return this.renderer;
10101         },
10102
10103         /**
10104          * This is an overrideable method which returns a reference to a default
10105          * renderer class if none is specified when creating the Ext.Updater.
10106          * Defaults to {@link Ext.Updater.BasicRenderer}
10107          */
10108         getDefaultRenderer: function() {
10109             return new Ext.Updater.BasicRenderer();
10110         },
10111
10112         /**
10113          * Sets the default URL used for updates.
10114          * @param {String/Function} defaultUrl The url or a function to call to get the url
10115          */
10116         setDefaultUrl : function(defaultUrl){
10117             this.defaultUrl = defaultUrl;
10118         },
10119
10120         /**
10121          * Get the Element this Updater is bound to
10122          * @return {Ext.Element} The element
10123          */
10124         getEl : function(){
10125             return this.el;
10126         },
10127
10128         /**
10129          * Performs an <b>asynchronous</b> request, updating this element with the response.
10130          * If params are specified it uses POST, otherwise it uses GET.<br><br>
10131          * <b>Note:</b> Due to the asynchronous nature of remote server requests, the Element
10132          * will not have been fully updated when the function returns. To post-process the returned
10133          * data, use the callback option, or an <b><code>update</code></b> event handler.
10134          * @param {Object} options A config object containing any of the following options:<ul>
10135          * <li>url : <b>String/Function</b><p class="sub-desc">The URL to request or a function which
10136          * <i>returns</i> the URL (defaults to the value of {@link Ext.Ajax#url} if not specified).</p></li>
10137          * <li>method : <b>String</b><p class="sub-desc">The HTTP method to
10138          * use. Defaults to POST if the <code>params</code> argument is present, otherwise GET.</p></li>
10139          * <li>params : <b>String/Object/Function</b><p class="sub-desc">The
10140          * parameters to pass to the server (defaults to none). These may be specified as a url-encoded
10141          * string, or as an object containing properties which represent parameters,
10142          * or as a function, which returns such an object.</p></li>
10143          * <li>scripts : <b>Boolean</b><p class="sub-desc">If <code>true</code>
10144          * any &lt;script&gt; tags embedded in the response text will be extracted
10145          * and executed (defaults to {@link Ext.Updater.defaults#loadScripts}). If this option is specified,
10146          * the callback will be called <i>after</i> the execution of the scripts.</p></li>
10147          * <li>callback : <b>Function</b><p class="sub-desc">A function to
10148          * be called when the response from the server arrives. The following
10149          * parameters are passed:<ul>
10150          * <li><b>el</b> : Ext.Element<p class="sub-desc">The Element being updated.</p></li>
10151          * <li><b>success</b> : Boolean<p class="sub-desc">True for success, false for failure.</p></li>
10152          * <li><b>response</b> : XMLHttpRequest<p class="sub-desc">The XMLHttpRequest which processed the update.</p></li>
10153          * <li><b>options</b> : Object<p class="sub-desc">The config object passed to the update call.</p></li></ul>
10154          * </p></li>
10155          * <li>scope : <b>Object</b><p class="sub-desc">The scope in which
10156          * to execute the callback (The callback's <code>this</code> reference.) If the
10157          * <code>params</code> argument is a function, this scope is used for that function also.</p></li>
10158          * <li>discardUrl : <b>Boolean</b><p class="sub-desc">By default, the URL of this request becomes
10159          * the default URL for this Updater object, and will be subsequently used in {@link #refresh}
10160          * calls.  To bypass this behavior, pass <code>discardUrl:true</code> (defaults to false).</p></li>
10161          * <li>timeout : <b>Number</b><p class="sub-desc">The number of seconds to wait for a response before
10162          * timing out (defaults to {@link Ext.Updater.defaults#timeout}).</p></li>
10163          * <li>text : <b>String</b><p class="sub-desc">The text to use as the innerHTML of the
10164          * {@link Ext.Updater.defaults#indicatorText} div (defaults to 'Loading...').  To replace the entire div, not
10165          * just the text, override {@link Ext.Updater.defaults#indicatorText} directly.</p></li>
10166          * <li>nocache : <b>Boolean</b><p class="sub-desc">Only needed for GET
10167          * requests, this option causes an extra, auto-generated parameter to be appended to the request
10168          * to defeat caching (defaults to {@link Ext.Updater.defaults#disableCaching}).</p></li></ul>
10169          * <p>
10170          * For example:
10171     <pre><code>
10172     um.update({
10173         url: "your-url.php",
10174         params: {param1: "foo", param2: "bar"}, // or a URL encoded string
10175         callback: yourFunction,
10176         scope: yourObject, //(optional scope)
10177         discardUrl: true,
10178         nocache: true,
10179         text: "Loading...",
10180         timeout: 60,
10181         scripts: false // Save time by avoiding RegExp execution.
10182     });
10183     </code></pre>
10184          */
10185         update : function(url, params, callback, discardUrl){
10186             var me = this,
10187                 cfg,
10188                 callerScope;
10189
10190             if(me.fireEvent(BEFOREUPDATE, me.el, url, params) !== false){
10191                 if(Ext.isObject(url)){ // must be config object
10192                     cfg = url;
10193                     url = cfg.url;
10194                     params = params || cfg.params;
10195                     callback = callback || cfg.callback;
10196                     discardUrl = discardUrl || cfg.discardUrl;
10197                     callerScope = cfg.scope;
10198                     if(!Ext.isEmpty(cfg.nocache)){me.disableCaching = cfg.nocache;};
10199                     if(!Ext.isEmpty(cfg.text)){me.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
10200                     if(!Ext.isEmpty(cfg.scripts)){me.loadScripts = cfg.scripts;};
10201                     if(!Ext.isEmpty(cfg.timeout)){me.timeout = cfg.timeout;};
10202                 }
10203                 me.showLoading();
10204
10205                 if(!discardUrl){
10206                     me.defaultUrl = url;
10207                 }
10208                 if(Ext.isFunction(url)){
10209                     url = url.call(me);
10210                 }
10211
10212                 var o = Ext.apply({}, {
10213                     url : url,
10214                     params: (Ext.isFunction(params) && callerScope) ? params.createDelegate(callerScope) : params,
10215                     success: processSuccess,
10216                     failure: processFailure,
10217                     scope: me,
10218                     callback: undefined,
10219                     timeout: (me.timeout*1000),
10220                     disableCaching: me.disableCaching,
10221                     argument: {
10222                         "options": cfg,
10223                         "url": url,
10224                         "form": null,
10225                         "callback": callback,
10226                         "scope": callerScope || window,
10227                         "params": params
10228                     }
10229                 }, cfg);
10230
10231                 me.transaction = Ext.Ajax.request(o);
10232             }
10233         },
10234
10235         /**
10236          * <p>Performs an asynchronous form post, updating this element with the response. If the form has the attribute
10237          * enctype="<a href="http://www.faqs.org/rfcs/rfc2388.html">multipart/form-data</a>", it assumes it's a file upload.
10238          * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.</p>
10239          * <p>File uploads are not performed using normal "Ajax" techniques, that is they are <b>not</b>
10240          * performed using XMLHttpRequests. Instead the form is submitted in the standard manner with the
10241          * DOM <code>&lt;form></code> element temporarily modified to have its
10242          * <a href="http://www.w3.org/TR/REC-html40/present/frames.html#adef-target">target</a> set to refer
10243          * to a dynamically generated, hidden <code>&lt;iframe></code> which is inserted into the document
10244          * but removed after the return data has been gathered.</p>
10245          * <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>
10246          * and some server technologies (notably JEE) may require some custom processing in order to
10247          * retrieve parameter names and parameter values from the packet content.</p>
10248          * @param {String/HTMLElement} form The form Id or form element
10249          * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
10250          * @param {Boolean} reset (optional) Whether to try to reset the form after the update
10251          * @param {Function} callback (optional) Callback when transaction is complete. The following
10252          * parameters are passed:<ul>
10253          * <li><b>el</b> : Ext.Element<p class="sub-desc">The Element being updated.</p></li>
10254          * <li><b>success</b> : Boolean<p class="sub-desc">True for success, false for failure.</p></li>
10255          * <li><b>response</b> : XMLHttpRequest<p class="sub-desc">The XMLHttpRequest which processed the update.</p></li></ul>
10256          */
10257         formUpdate : function(form, url, reset, callback){
10258             var me = this;
10259             if(me.fireEvent(BEFOREUPDATE, me.el, form, url) !== false){
10260                 if(Ext.isFunction(url)){
10261                     url = url.call(me);
10262                 }
10263                 form = Ext.getDom(form);
10264                 me.transaction = Ext.Ajax.request({
10265                     form: form,
10266                     url:url,
10267                     success: processSuccess,
10268                     failure: processFailure,
10269                     scope: me,
10270                     timeout: (me.timeout*1000),
10271                     argument: {
10272                         "url": url,
10273                         "form": form,
10274                         "callback": callback,
10275                         "reset": reset
10276                     }
10277                 });
10278                 me.showLoading.defer(1, me);
10279             }
10280         },
10281
10282         /**
10283          * Set this element to auto refresh.  Can be canceled by calling {@link #stopAutoRefresh}.
10284          * @param {Number} interval How often to update (in seconds).
10285          * @param {String/Object/Function} url (optional) The url for this request, a config object in the same format
10286          * supported by {@link #load}, or a function to call to get the url (defaults to the last used url).  Note that while
10287          * the url used in a load call can be reused by this method, other load config options will not be reused and must be
10288          * sepcified as part of a config object passed as this paramter if needed.
10289          * @param {String/Object} params (optional) The parameters to pass as either a url encoded string
10290          * "&param1=1&param2=2" or as an object {param1: 1, param2: 2}
10291          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
10292          * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
10293          */
10294         startAutoRefresh : function(interval, url, params, callback, refreshNow){
10295             var me = this;
10296             if(refreshNow){
10297                 me.update(url || me.defaultUrl, params, callback, true);
10298             }
10299             if(me.autoRefreshProcId){
10300                 clearInterval(me.autoRefreshProcId);
10301             }
10302             me.autoRefreshProcId = setInterval(me.update.createDelegate(me, [url || me.defaultUrl, params, callback, true]), interval * 1000);
10303         },
10304
10305         /**
10306          * Stop auto refresh on this element.
10307          */
10308         stopAutoRefresh : function(){
10309             if(this.autoRefreshProcId){
10310                 clearInterval(this.autoRefreshProcId);
10311                 delete this.autoRefreshProcId;
10312             }
10313         },
10314
10315         /**
10316          * Returns true if the Updater is currently set to auto refresh its content (see {@link #startAutoRefresh}), otherwise false.
10317          */
10318         isAutoRefreshing : function(){
10319            return !!this.autoRefreshProcId;
10320         },
10321
10322         /**
10323          * Display the element's "loading" state. By default, the element is updated with {@link #indicatorText}. This
10324          * method may be overridden to perform a custom action while this Updater is actively updating its contents.
10325          */
10326         showLoading : function(){
10327             if(this.showLoadIndicator){
10328                 this.el.dom.innerHTML = this.indicatorText;
10329             }
10330         },
10331
10332         /**
10333          * Aborts the currently executing transaction, if any.
10334          */
10335         abort : function(){
10336             if(this.transaction){
10337                 Ext.Ajax.abort(this.transaction);
10338             }
10339         },
10340
10341         /**
10342          * Returns true if an update is in progress, otherwise false.
10343          * @return {Boolean}
10344          */
10345         isUpdating : function(){
10346             return this.transaction ? Ext.Ajax.isLoading(this.transaction) : false;
10347         },
10348
10349         /**
10350          * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
10351          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
10352          */
10353         refresh : function(callback){
10354             if(this.defaultUrl){
10355                 this.update(this.defaultUrl, null, callback, true);
10356             }
10357         }
10358     }
10359 }());
10360
10361 /**
10362  * @class Ext.Updater.defaults
10363  * The defaults collection enables customizing the default properties of Updater
10364  */
10365 Ext.Updater.defaults = {
10366    /**
10367      * Timeout for requests or form posts in seconds (defaults to 30 seconds).
10368      * @type Number
10369      */
10370     timeout : 30,
10371     /**
10372      * True to append a unique parameter to GET requests to disable caching (defaults to false).
10373      * @type Boolean
10374      */
10375     disableCaching : false,
10376     /**
10377      * Whether or not to show {@link #indicatorText} during loading (defaults to true).
10378      * @type Boolean
10379      */
10380     showLoadIndicator : true,
10381     /**
10382      * Text for loading indicator (defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
10383      * @type String
10384      */
10385     indicatorText : '<div class="loading-indicator">Loading...</div>',
10386      /**
10387      * True to process scripts by default (defaults to false).
10388      * @type Boolean
10389      */
10390     loadScripts : false,
10391     /**
10392     * Blank page URL to use with SSL file uploads (defaults to {@link Ext#SSL_SECURE_URL} if set, or "javascript:false").
10393     * @type String
10394     */
10395     sslBlankUrl : Ext.SSL_SECURE_URL
10396 };
10397
10398
10399 /**
10400  * Static convenience method. <b>This method is deprecated in favor of el.load({url:'foo.php', ...})</b>.
10401  * Usage:
10402  * <pre><code>Ext.Updater.updateElement("my-div", "stuff.php");</code></pre>
10403  * @param {Mixed} el The element to update
10404  * @param {String} url The url
10405  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
10406  * @param {Object} options (optional) A config object with any of the Updater properties you want to set - for
10407  * example: {disableCaching:true, indicatorText: "Loading data..."}
10408  * @static
10409  * @deprecated
10410  * @member Ext.Updater
10411  */
10412 Ext.Updater.updateElement = function(el, url, params, options){
10413     var um = Ext.get(el).getUpdater();
10414     Ext.apply(um, options);
10415     um.update(url, params, options ? options.callback : null);
10416 };
10417
10418 /**
10419  * @class Ext.Updater.BasicRenderer
10420  * <p>This class is a base class implementing a simple render method which updates an element using results from an Ajax request.</p>
10421  * <p>The BasicRenderer updates the element's innerHTML with the responseText. To perform a custom render (i.e. XML or JSON processing),
10422  * create an object with a conforming {@link #render} method and pass it to setRenderer on the Updater.</p>
10423  */
10424 Ext.Updater.BasicRenderer = function(){};
10425
10426 Ext.Updater.BasicRenderer.prototype = {
10427     /**
10428      * This method is called when an Ajax response is received, and an Element needs updating.
10429      * @param {Ext.Element} el The element being rendered
10430      * @param {Object} xhr The XMLHttpRequest object
10431      * @param {Updater} updateManager The calling update manager
10432      * @param {Function} callback A callback that will need to be called if loadScripts is true on the Updater
10433      */
10434      render : function(el, response, updateManager, callback){
10435         el.update(response.responseText, updateManager.loadScripts, callback);
10436     }
10437 };/**
10438  * @class Date
10439  *
10440  * The date parsing and formatting syntax contains a subset of
10441  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
10442  * supported will provide results equivalent to their PHP versions.
10443  *
10444  * The following is a list of all currently supported formats:
10445  * <pre>
10446 Format  Description                                                               Example returned values
10447 ------  -----------------------------------------------------------------------   -----------------------
10448   d     Day of the month, 2 digits with leading zeros                             01 to 31
10449   D     A short textual representation of the day of the week                     Mon to Sun
10450   j     Day of the month without leading zeros                                    1 to 31
10451   l     A full textual representation of the day of the week                      Sunday to Saturday
10452   N     ISO-8601 numeric representation of the day of the week                    1 (for Monday) through 7 (for Sunday)
10453   S     English ordinal suffix for the day of the month, 2 characters             st, nd, rd or th. Works well with j
10454   w     Numeric representation of the day of the week                             0 (for Sunday) to 6 (for Saturday)
10455   z     The day of the year (starting from 0)                                     0 to 364 (365 in leap years)
10456   W     ISO-8601 week number of year, weeks starting on Monday                    01 to 53
10457   F     A full textual representation of a month, such as January or March        January to December
10458   m     Numeric representation of a month, with leading zeros                     01 to 12
10459   M     A short textual representation of a month                                 Jan to Dec
10460   n     Numeric representation of a month, without leading zeros                  1 to 12
10461   t     Number of days in the given month                                         28 to 31
10462   L     Whether it's a leap year                                                  1 if it is a leap year, 0 otherwise.
10463   o     ISO-8601 year number (identical to (Y), but if the ISO week number (W)    Examples: 1998 or 2004
10464         belongs to the previous or next year, that year is used instead)
10465   Y     A full numeric representation of a year, 4 digits                         Examples: 1999 or 2003
10466   y     A two digit representation of a year                                      Examples: 99 or 03
10467   a     Lowercase Ante meridiem and Post meridiem                                 am or pm
10468   A     Uppercase Ante meridiem and Post meridiem                                 AM or PM
10469   g     12-hour format of an hour without leading zeros                           1 to 12
10470   G     24-hour format of an hour without leading zeros                           0 to 23
10471   h     12-hour format of an hour with leading zeros                              01 to 12
10472   H     24-hour format of an hour with leading zeros                              00 to 23
10473   i     Minutes, with leading zeros                                               00 to 59
10474   s     Seconds, with leading zeros                                               00 to 59
10475   u     Decimal fraction of a second                                              Examples:
10476         (minimum 1 digit, arbitrary number of digits allowed)                     001 (i.e. 0.001s) or
10477                                                                                   100 (i.e. 0.100s) or
10478                                                                                   999 (i.e. 0.999s) or
10479                                                                                   999876543210 (i.e. 0.999876543210s)
10480   O     Difference to Greenwich time (GMT) in hours and minutes                   Example: +1030
10481   P     Difference to Greenwich time (GMT) with colon between hours and minutes   Example: -08:00
10482   T     Timezone abbreviation of the machine running the code                     Examples: EST, MDT, PDT ...
10483   Z     Timezone offset in seconds (negative if west of UTC, positive if east)    -43200 to 50400
10484   c     ISO 8601 date
10485         Notes:                                                                    Examples:
10486         1) If unspecified, the month / day defaults to the current month / day,   1991 or
10487            the time defaults to midnight, while the timezone defaults to the      1992-10 or
10488            browser's timezone. If a time is specified, it must include both hours 1993-09-20 or
10489            and minutes. The "T" delimiter, seconds, milliseconds and timezone     1994-08-19T16:20+01:00 or
10490            are optional.                                                          1995-07-18T17:21:28-02:00 or
10491         2) The decimal fraction of a second, if specified, must contain at        1996-06-17T18:22:29.98765+03:00 or
10492            least 1 digit (there is no limit to the maximum number                 1997-05-16T19:23:30,12345-0400 or
10493            of digits allowed), and may be delimited by either a '.' or a ','      1998-04-15T20:24:31.2468Z or
10494         Refer to the examples on the right for the various levels of              1999-03-14T20:24:32Z or
10495         date-time granularity which are supported, or see                         2000-02-13T21:25:33
10496         http://www.w3.org/TR/NOTE-datetime for more info.                         2001-01-12 22:26:34
10497   U     Seconds since the Unix Epoch (January 1 1970 00:00:00 GMT)                1193432466 or -2138434463
10498   M$    Microsoft AJAX serialized dates                                           \/Date(1238606590509)\/ (i.e. UTC milliseconds since epoch) or
10499                                                                                   \/Date(1238606590509+0800)\/
10500 </pre>
10501  *
10502  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
10503  * <pre><code>
10504 // Sample date:
10505 // 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
10506
10507 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
10508 document.write(dt.format('Y-m-d'));                           // 2007-01-10
10509 document.write(dt.format('F j, Y, g:i a'));                   // January 10, 2007, 3:05 pm
10510 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
10511 </code></pre>
10512  *
10513  * Here are some standard date/time patterns that you might find helpful.  They
10514  * are not part of the source of Date.js, but to use them you can simply copy this
10515  * block of code into any script that is included after Date.js and they will also become
10516  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
10517  * <pre><code>
10518 Date.patterns = {
10519     ISO8601Long:"Y-m-d H:i:s",
10520     ISO8601Short:"Y-m-d",
10521     ShortDate: "n/j/Y",
10522     LongDate: "l, F d, Y",
10523     FullDateTime: "l, F d, Y g:i:s A",
10524     MonthDay: "F d",
10525     ShortTime: "g:i A",
10526     LongTime: "g:i:s A",
10527     SortableDateTime: "Y-m-d\\TH:i:s",
10528     UniversalSortableDateTime: "Y-m-d H:i:sO",
10529     YearMonth: "F, Y"
10530 };
10531 </code></pre>
10532  *
10533  * Example usage:
10534  * <pre><code>
10535 var dt = new Date();
10536 document.write(dt.format(Date.patterns.ShortDate));
10537 </code></pre>
10538  * <p>Developer-written, custom formats may be used by supplying both a formatting and a parsing function
10539  * which perform to specialized requirements. The functions are stored in {@link #parseFunctions} and {@link #formatFunctions}.</p>
10540  */
10541
10542 /*
10543  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
10544  * (see http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/)
10545  * They generate precompiled functions from format patterns instead of parsing and
10546  * processing each pattern every time a date is formatted. These functions are available
10547  * on every Date object.
10548  */
10549
10550 (function() {
10551
10552 /**
10553  * Global flag which determines if strict date parsing should be used.
10554  * Strict date parsing will not roll-over invalid dates, which is the
10555  * default behaviour of javascript Date objects.
10556  * (see {@link #parseDate} for more information)
10557  * Defaults to <tt>false</tt>.
10558  * @static
10559  * @type Boolean
10560 */
10561 Date.useStrict = false;
10562
10563
10564 // create private copy of Ext's String.format() method
10565 // - to remove unnecessary dependency
10566 // - to resolve namespace conflict with M$-Ajax's implementation
10567 function xf(format) {
10568     var args = Array.prototype.slice.call(arguments, 1);
10569     return format.replace(/\{(\d+)\}/g, function(m, i) {
10570         return args[i];
10571     });
10572 }
10573
10574
10575 // private
10576 Date.formatCodeToRegex = function(character, currentGroup) {
10577     // Note: currentGroup - position in regex result array (see notes for Date.parseCodes below)
10578     var p = Date.parseCodes[character];
10579
10580     if (p) {
10581       p = typeof p == 'function'? p() : p;
10582       Date.parseCodes[character] = p; // reassign function result to prevent repeated execution
10583     }
10584
10585     return p ? Ext.applyIf({
10586       c: p.c ? xf(p.c, currentGroup || "{0}") : p.c
10587     }, p) : {
10588         g:0,
10589         c:null,
10590         s:Ext.escapeRe(character) // treat unrecognised characters as literals
10591     }
10592 };
10593
10594 // private shorthand for Date.formatCodeToRegex since we'll be using it fairly often
10595 var $f = Date.formatCodeToRegex;
10596
10597 Ext.apply(Date, {
10598     /**
10599      * <p>An object hash in which each property is a date parsing function. The property name is the
10600      * format string which that function parses.</p>
10601      * <p>This object is automatically populated with date parsing functions as
10602      * date formats are requested for Ext standard formatting strings.</p>
10603      * <p>Custom parsing functions may be inserted into this object, keyed by a name which from then on
10604      * may be used as a format string to {@link #parseDate}.<p>
10605      * <p>Example:</p><pre><code>
10606 Date.parseFunctions['x-date-format'] = myDateParser;
10607 </code></pre>
10608      * <p>A parsing function should return a Date object, and is passed the following parameters:<div class="mdetail-params"><ul>
10609      * <li><code>date</code> : String<div class="sub-desc">The date string to parse.</div></li>
10610      * <li><code>strict</code> : Boolean<div class="sub-desc">True to validate date strings while parsing
10611      * (i.e. prevent javascript Date "rollover") (The default must be false).
10612      * Invalid date strings should return null when parsed.</div></li>
10613      * </ul></div></p>
10614      * <p>To enable Dates to also be <i>formatted</i> according to that format, a corresponding
10615      * formatting function must be placed into the {@link #formatFunctions} property.
10616      * @property parseFunctions
10617      * @static
10618      * @type Object
10619      */
10620     parseFunctions: {
10621         "M$": function(input, strict) {
10622             // note: the timezone offset is ignored since the M$ Ajax server sends
10623             // a UTC milliseconds-since-Unix-epoch value (negative values are allowed)
10624             var re = new RegExp('\\/Date\\(([-+])?(\\d+)(?:[+-]\\d{4})?\\)\\/');
10625             var r = (input || '').match(re);
10626             return r? new Date(((r[1] || '') + r[2]) * 1) : null;
10627         }
10628     },
10629     parseRegexes: [],
10630
10631     /**
10632      * <p>An object hash in which each property is a date formatting function. The property name is the
10633      * format string which corresponds to the produced formatted date string.</p>
10634      * <p>This object is automatically populated with date formatting functions as
10635      * date formats are requested for Ext standard formatting strings.</p>
10636      * <p>Custom formatting functions may be inserted into this object, keyed by a name which from then on
10637      * may be used as a format string to {@link #format}. Example:</p><pre><code>
10638 Date.formatFunctions['x-date-format'] = myDateFormatter;
10639 </code></pre>
10640      * <p>A formatting function should return a string representation of the passed Date object, and is passed the following parameters:<div class="mdetail-params"><ul>
10641      * <li><code>date</code> : Date<div class="sub-desc">The Date to format.</div></li>
10642      * </ul></div></p>
10643      * <p>To enable date strings to also be <i>parsed</i> according to that format, a corresponding
10644      * parsing function must be placed into the {@link #parseFunctions} property.
10645      * @property formatFunctions
10646      * @static
10647      * @type Object
10648      */
10649     formatFunctions: {
10650         "M$": function() {
10651             // UTC milliseconds since Unix epoch (M$-AJAX serialized date format (MRSF))
10652             return '\\/Date(' + this.getTime() + ')\\/';
10653         }
10654     },
10655
10656     y2kYear : 50,
10657
10658     /**
10659      * Date interval constant
10660      * @static
10661      * @type String
10662      */
10663     MILLI : "ms",
10664
10665     /**
10666      * Date interval constant
10667      * @static
10668      * @type String
10669      */
10670     SECOND : "s",
10671
10672     /**
10673      * Date interval constant
10674      * @static
10675      * @type String
10676      */
10677     MINUTE : "mi",
10678
10679     /** Date interval constant
10680      * @static
10681      * @type String
10682      */
10683     HOUR : "h",
10684
10685     /**
10686      * Date interval constant
10687      * @static
10688      * @type String
10689      */
10690     DAY : "d",
10691
10692     /**
10693      * Date interval constant
10694      * @static
10695      * @type String
10696      */
10697     MONTH : "mo",
10698
10699     /**
10700      * Date interval constant
10701      * @static
10702      * @type String
10703      */
10704     YEAR : "y",
10705
10706     /**
10707      * <p>An object hash containing default date values used during date parsing.</p>
10708      * <p>The following properties are available:<div class="mdetail-params"><ul>
10709      * <li><code>y</code> : Number<div class="sub-desc">The default year value. (defaults to undefined)</div></li>
10710      * <li><code>m</code> : Number<div class="sub-desc">The default 1-based month value. (defaults to undefined)</div></li>
10711      * <li><code>d</code> : Number<div class="sub-desc">The default day value. (defaults to undefined)</div></li>
10712      * <li><code>h</code> : Number<div class="sub-desc">The default hour value. (defaults to undefined)</div></li>
10713      * <li><code>i</code> : Number<div class="sub-desc">The default minute value. (defaults to undefined)</div></li>
10714      * <li><code>s</code> : Number<div class="sub-desc">The default second value. (defaults to undefined)</div></li>
10715      * <li><code>ms</code> : Number<div class="sub-desc">The default millisecond value. (defaults to undefined)</div></li>
10716      * </ul></div></p>
10717      * <p>Override these properties to customize the default date values used by the {@link #parseDate} method.</p>
10718      * <p><b>Note: In countries which experience Daylight Saving Time (i.e. DST), the <tt>h</tt>, <tt>i</tt>, <tt>s</tt>
10719      * and <tt>ms</tt> properties may coincide with the exact time in which DST takes effect.
10720      * It is the responsiblity of the developer to account for this.</b></p>
10721      * Example Usage:
10722      * <pre><code>
10723 // set default day value to the first day of the month
10724 Date.defaults.d = 1;
10725
10726 // parse a February date string containing only year and month values.
10727 // setting the default day value to 1 prevents weird date rollover issues
10728 // when attempting to parse the following date string on, for example, March 31st 2009.
10729 Date.parseDate('2009-02', 'Y-m'); // returns a Date object representing February 1st 2009
10730 </code></pre>
10731      * @property defaults
10732      * @static
10733      * @type Object
10734      */
10735     defaults: {},
10736
10737     /**
10738      * An array of textual day names.
10739      * Override these values for international dates.
10740      * Example:
10741      * <pre><code>
10742 Date.dayNames = [
10743     'SundayInYourLang',
10744     'MondayInYourLang',
10745     ...
10746 ];
10747 </code></pre>
10748      * @type Array
10749      * @static
10750      */
10751     dayNames : [
10752         "Sunday",
10753         "Monday",
10754         "Tuesday",
10755         "Wednesday",
10756         "Thursday",
10757         "Friday",
10758         "Saturday"
10759     ],
10760
10761     /**
10762      * An array of textual month names.
10763      * Override these values for international dates.
10764      * Example:
10765      * <pre><code>
10766 Date.monthNames = [
10767     'JanInYourLang',
10768     'FebInYourLang',
10769     ...
10770 ];
10771 </code></pre>
10772      * @type Array
10773      * @static
10774      */
10775     monthNames : [
10776         "January",
10777         "February",
10778         "March",
10779         "April",
10780         "May",
10781         "June",
10782         "July",
10783         "August",
10784         "September",
10785         "October",
10786         "November",
10787         "December"
10788     ],
10789
10790     /**
10791      * An object hash of zero-based javascript month numbers (with short month names as keys. note: keys are case-sensitive).
10792      * Override these values for international dates.
10793      * Example:
10794      * <pre><code>
10795 Date.monthNumbers = {
10796     'ShortJanNameInYourLang':0,
10797     'ShortFebNameInYourLang':1,
10798     ...
10799 };
10800 </code></pre>
10801      * @type Object
10802      * @static
10803      */
10804     monthNumbers : {
10805         Jan:0,
10806         Feb:1,
10807         Mar:2,
10808         Apr:3,
10809         May:4,
10810         Jun:5,
10811         Jul:6,
10812         Aug:7,
10813         Sep:8,
10814         Oct:9,
10815         Nov:10,
10816         Dec:11
10817     },
10818
10819     /**
10820      * Get the short month name for the given month number.
10821      * Override this function for international dates.
10822      * @param {Number} month A zero-based javascript month number.
10823      * @return {String} The short month name.
10824      * @static
10825      */
10826     getShortMonthName : function(month) {
10827         return Date.monthNames[month].substring(0, 3);
10828     },
10829
10830     /**
10831      * Get the short day name for the given day number.
10832      * Override this function for international dates.
10833      * @param {Number} day A zero-based javascript day number.
10834      * @return {String} The short day name.
10835      * @static
10836      */
10837     getShortDayName : function(day) {
10838         return Date.dayNames[day].substring(0, 3);
10839     },
10840
10841     /**
10842      * Get the zero-based javascript month number for the given short/full month name.
10843      * Override this function for international dates.
10844      * @param {String} name The short/full month name.
10845      * @return {Number} The zero-based javascript month number.
10846      * @static
10847      */
10848     getMonthNumber : function(name) {
10849         // handle camel casing for english month names (since the keys for the Date.monthNumbers hash are case sensitive)
10850         return Date.monthNumbers[name.substring(0, 1).toUpperCase() + name.substring(1, 3).toLowerCase()];
10851     },
10852
10853     /**
10854      * The base format-code to formatting-function hashmap used by the {@link #format} method.
10855      * Formatting functions are strings (or functions which return strings) which
10856      * will return the appropriate value when evaluated in the context of the Date object
10857      * from which the {@link #format} method is called.
10858      * Add to / override these mappings for custom date formatting.
10859      * Note: Date.format() treats characters as literals if an appropriate mapping cannot be found.
10860      * Example:
10861      * <pre><code>
10862 Date.formatCodes.x = "String.leftPad(this.getDate(), 2, '0')";
10863 (new Date()).format("X"); // returns the current day of the month
10864 </code></pre>
10865      * @type Object
10866      * @static
10867      */
10868     formatCodes : {
10869         d: "String.leftPad(this.getDate(), 2, '0')",
10870         D: "Date.getShortDayName(this.getDay())", // get localised short day name
10871         j: "this.getDate()",
10872         l: "Date.dayNames[this.getDay()]",
10873         N: "(this.getDay() ? this.getDay() : 7)",
10874         S: "this.getSuffix()",
10875         w: "this.getDay()",
10876         z: "this.getDayOfYear()",
10877         W: "String.leftPad(this.getWeekOfYear(), 2, '0')",
10878         F: "Date.monthNames[this.getMonth()]",
10879         m: "String.leftPad(this.getMonth() + 1, 2, '0')",
10880         M: "Date.getShortMonthName(this.getMonth())", // get localised short month name
10881         n: "(this.getMonth() + 1)",
10882         t: "this.getDaysInMonth()",
10883         L: "(this.isLeapYear() ? 1 : 0)",
10884         o: "(this.getFullYear() + (this.getWeekOfYear() == 1 && this.getMonth() > 0 ? +1 : (this.getWeekOfYear() >= 52 && this.getMonth() < 11 ? -1 : 0)))",
10885         Y: "this.getFullYear()",
10886         y: "('' + this.getFullYear()).substring(2, 4)",
10887         a: "(this.getHours() < 12 ? 'am' : 'pm')",
10888         A: "(this.getHours() < 12 ? 'AM' : 'PM')",
10889         g: "((this.getHours() % 12) ? this.getHours() % 12 : 12)",
10890         G: "this.getHours()",
10891         h: "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0')",
10892         H: "String.leftPad(this.getHours(), 2, '0')",
10893         i: "String.leftPad(this.getMinutes(), 2, '0')",
10894         s: "String.leftPad(this.getSeconds(), 2, '0')",
10895         u: "String.leftPad(this.getMilliseconds(), 3, '0')",
10896         O: "this.getGMTOffset()",
10897         P: "this.getGMTOffset(true)",
10898         T: "this.getTimezone()",
10899         Z: "(this.getTimezoneOffset() * -60)",
10900
10901         c: function() { // ISO-8601 -- GMT format
10902             for (var c = "Y-m-dTH:i:sP", code = [], i = 0, l = c.length; i < l; ++i) {
10903                 var e = c.charAt(i);
10904                 code.push(e == "T" ? "'T'" : Date.getFormatCode(e)); // treat T as a character literal
10905             }
10906             return code.join(" + ");
10907         },
10908         /*
10909         c: function() { // ISO-8601 -- UTC format
10910             return [
10911               "this.getUTCFullYear()", "'-'",
10912               "String.leftPad(this.getUTCMonth() + 1, 2, '0')", "'-'",
10913               "String.leftPad(this.getUTCDate(), 2, '0')",
10914               "'T'",
10915               "String.leftPad(this.getUTCHours(), 2, '0')", "':'",
10916               "String.leftPad(this.getUTCMinutes(), 2, '0')", "':'",
10917               "String.leftPad(this.getUTCSeconds(), 2, '0')",
10918               "'Z'"
10919             ].join(" + ");
10920         },
10921         */
10922
10923         U: "Math.round(this.getTime() / 1000)"
10924     },
10925
10926     /**
10927      * Checks if the passed Date parameters will cause a javascript Date "rollover".
10928      * @param {Number} year 4-digit year
10929      * @param {Number} month 1-based month-of-year
10930      * @param {Number} day Day of month
10931      * @param {Number} hour (optional) Hour
10932      * @param {Number} minute (optional) Minute
10933      * @param {Number} second (optional) Second
10934      * @param {Number} millisecond (optional) Millisecond
10935      * @return {Boolean} true if the passed parameters do not cause a Date "rollover", false otherwise.
10936      * @static
10937      */
10938     isValid : function(y, m, d, h, i, s, ms) {
10939         // setup defaults
10940         h = h || 0;
10941         i = i || 0;
10942         s = s || 0;
10943         ms = ms || 0;
10944
10945         var dt = new Date(y, m - 1, d, h, i, s, ms);
10946
10947         return y == dt.getFullYear() &&
10948             m == dt.getMonth() + 1 &&
10949             d == dt.getDate() &&
10950             h == dt.getHours() &&
10951             i == dt.getMinutes() &&
10952             s == dt.getSeconds() &&
10953             ms == dt.getMilliseconds();
10954     },
10955
10956     /**
10957      * Parses the passed string using the specified date format.
10958      * Note that this function expects normal calendar dates, meaning that months are 1-based (i.e. 1 = January).
10959      * The {@link #defaults} hash will be used for any date value (i.e. year, month, day, hour, minute, second or millisecond)
10960      * which cannot be found in the passed string. If a corresponding default date value has not been specified in the {@link #defaults} hash,
10961      * the current date's year, month, day or DST-adjusted zero-hour time value will be used instead.
10962      * Keep in mind that the input date string must precisely match the specified format string
10963      * in order for the parse operation to be successful (failed parse operations return a null value).
10964      * <p>Example:</p><pre><code>
10965 //dt = Fri May 25 2007 (current date)
10966 var dt = new Date();
10967
10968 //dt = Thu May 25 2006 (today&#39;s month/day in 2006)
10969 dt = Date.parseDate("2006", "Y");
10970
10971 //dt = Sun Jan 15 2006 (all date parts specified)
10972 dt = Date.parseDate("2006-01-15", "Y-m-d");
10973
10974 //dt = Sun Jan 15 2006 15:20:01
10975 dt = Date.parseDate("2006-01-15 3:20:01 PM", "Y-m-d g:i:s A");
10976
10977 // attempt to parse Sun Feb 29 2006 03:20:01 in strict mode
10978 dt = Date.parseDate("2006-02-29 03:20:01", "Y-m-d H:i:s", true); // returns null
10979 </code></pre>
10980      * @param {String} input The raw date string.
10981      * @param {String} format The expected date string format.
10982      * @param {Boolean} strict (optional) True to validate date strings while parsing (i.e. prevents javascript Date "rollover")
10983                         (defaults to false). Invalid date strings will return null when parsed.
10984      * @return {Date} The parsed Date.
10985      * @static
10986      */
10987     parseDate : function(input, format, strict) {
10988         var p = Date.parseFunctions;
10989         if (p[format] == null) {
10990             Date.createParser(format);
10991         }
10992         return p[format](input, Ext.isDefined(strict) ? strict : Date.useStrict);
10993     },
10994
10995     // private
10996     getFormatCode : function(character) {
10997         var f = Date.formatCodes[character];
10998
10999         if (f) {
11000           f = typeof f == 'function'? f() : f;
11001           Date.formatCodes[character] = f; // reassign function result to prevent repeated execution
11002         }
11003
11004         // note: unknown characters are treated as literals
11005         return f || ("'" + String.escape(character) + "'");
11006     },
11007
11008     // private
11009     createFormat : function(format) {
11010         var code = [],
11011             special = false,
11012             ch = '';
11013
11014         for (var i = 0; i < format.length; ++i) {
11015             ch = format.charAt(i);
11016             if (!special && ch == "\\") {
11017                 special = true;
11018             } else if (special) {
11019                 special = false;
11020                 code.push("'" + String.escape(ch) + "'");
11021             } else {
11022                 code.push(Date.getFormatCode(ch))
11023             }
11024         }
11025         Date.formatFunctions[format] = new Function("return " + code.join('+'));
11026     },
11027
11028     // private
11029     createParser : function() {
11030         var code = [
11031             "var dt, y, m, d, h, i, s, ms, o, z, zz, u, v,",
11032                 "def = Date.defaults,",
11033                 "results = String(input).match(Date.parseRegexes[{0}]);", // either null, or an array of matched strings
11034
11035             "if(results){",
11036                 "{1}",
11037
11038                 "if(u != null){", // i.e. unix time is defined
11039                     "v = new Date(u * 1000);", // give top priority to UNIX time
11040                 "}else{",
11041                     // create Date object representing midnight of the current day;
11042                     // this will provide us with our date defaults
11043                     // (note: clearTime() handles Daylight Saving Time automatically)
11044                     "dt = (new Date()).clearTime();",
11045
11046                     // date calculations (note: these calculations create a dependency on Ext.num())
11047                     "y = Ext.num(y, Ext.num(def.y, dt.getFullYear()));",
11048                     "m = Ext.num(m, Ext.num(def.m - 1, dt.getMonth()));",
11049                     "d = Ext.num(d, Ext.num(def.d, dt.getDate()));",
11050
11051                     // time calculations (note: these calculations create a dependency on Ext.num())
11052                     "h  = Ext.num(h, Ext.num(def.h, dt.getHours()));",
11053                     "i  = Ext.num(i, Ext.num(def.i, dt.getMinutes()));",
11054                     "s  = Ext.num(s, Ext.num(def.s, dt.getSeconds()));",
11055                     "ms = Ext.num(ms, Ext.num(def.ms, dt.getMilliseconds()));",
11056
11057                     "if(z >= 0 && y >= 0){",
11058                         // both the year and zero-based day of year are defined and >= 0.
11059                         // these 2 values alone provide sufficient info to create a full date object
11060
11061                         // create Date object representing January 1st for the given year
11062                         "v = new Date(y, 0, 1, h, i, s, ms);",
11063
11064                         // then add day of year, checking for Date "rollover" if necessary
11065                         "v = !strict? v : (strict === true && (z <= 364 || (v.isLeapYear() && z <= 365))? v.add(Date.DAY, z) : null);",
11066                     "}else if(strict === true && !Date.isValid(y, m + 1, d, h, i, s, ms)){", // check for Date "rollover"
11067                         "v = null;", // invalid date, so return null
11068                     "}else{",
11069                         // plain old Date object
11070                         "v = new Date(y, m, d, h, i, s, ms);",
11071                     "}",
11072                 "}",
11073             "}",
11074
11075             "if(v){",
11076                 // favour UTC offset over GMT offset
11077                 "if(zz != null){",
11078                     // reset to UTC, then add offset
11079                     "v = v.add(Date.SECOND, -v.getTimezoneOffset() * 60 - zz);",
11080                 "}else if(o){",
11081                     // reset to GMT, then add offset
11082                     "v = v.add(Date.MINUTE, -v.getTimezoneOffset() + (sn == '+'? -1 : 1) * (hr * 60 + mn));",
11083                 "}",
11084             "}",
11085
11086             "return v;"
11087         ].join('\n');
11088
11089         return function(format) {
11090             var regexNum = Date.parseRegexes.length,
11091                 currentGroup = 1,
11092                 calc = [],
11093                 regex = [],
11094                 special = false,
11095                 ch = "";
11096
11097             for (var i = 0; i < format.length; ++i) {
11098                 ch = format.charAt(i);
11099                 if (!special && ch == "\\") {
11100                     special = true;
11101                 } else if (special) {
11102                     special = false;
11103                     regex.push(String.escape(ch));
11104                 } else {
11105                     var obj = $f(ch, currentGroup);
11106                     currentGroup += obj.g;
11107                     regex.push(obj.s);
11108                     if (obj.g && obj.c) {
11109                         calc.push(obj.c);
11110                     }
11111                 }
11112             }
11113
11114             Date.parseRegexes[regexNum] = new RegExp("^" + regex.join('') + "$");
11115             Date.parseFunctions[format] = new Function("input", "strict", xf(code, regexNum, calc.join('')));
11116         }
11117     }(),
11118
11119     // private
11120     parseCodes : {
11121         /*
11122          * Notes:
11123          * g = {Number} calculation group (0 or 1. only group 1 contributes to date calculations.)
11124          * c = {String} calculation method (required for group 1. null for group 0. {0} = currentGroup - position in regex result array)
11125          * s = {String} regex pattern. all matches are stored in results[], and are accessible by the calculation mapped to 'c'
11126          */
11127         d: {
11128             g:1,
11129             c:"d = parseInt(results[{0}], 10);\n",
11130             s:"(\\d{2})" // day of month with leading zeroes (01 - 31)
11131         },
11132         j: {
11133             g:1,
11134             c:"d = parseInt(results[{0}], 10);\n",
11135             s:"(\\d{1,2})" // day of month without leading zeroes (1 - 31)
11136         },
11137         D: function() {
11138             for (var a = [], i = 0; i < 7; a.push(Date.getShortDayName(i)), ++i); // get localised short day names
11139             return {
11140                 g:0,
11141                 c:null,
11142                 s:"(?:" + a.join("|") +")"
11143             }
11144         },
11145         l: function() {
11146             return {
11147                 g:0,
11148                 c:null,
11149                 s:"(?:" + Date.dayNames.join("|") + ")"
11150             }
11151         },
11152         N: {
11153             g:0,
11154             c:null,
11155             s:"[1-7]" // ISO-8601 day number (1 (monday) - 7 (sunday))
11156         },
11157         S: {
11158             g:0,
11159             c:null,
11160             s:"(?:st|nd|rd|th)"
11161         },
11162         w: {
11163             g:0,
11164             c:null,
11165             s:"[0-6]" // javascript day number (0 (sunday) - 6 (saturday))
11166         },
11167         z: {
11168             g:1,
11169             c:"z = parseInt(results[{0}], 10);\n",
11170             s:"(\\d{1,3})" // day of the year (0 - 364 (365 in leap years))
11171         },
11172         W: {
11173             g:0,
11174             c:null,
11175             s:"(?:\\d{2})" // ISO-8601 week number (with leading zero)
11176         },
11177         F: function() {
11178             return {
11179                 g:1,
11180                 c:"m = parseInt(Date.getMonthNumber(results[{0}]), 10);\n", // get localised month number
11181                 s:"(" + Date.monthNames.join("|") + ")"
11182             }
11183         },
11184         M: function() {
11185             for (var a = [], i = 0; i < 12; a.push(Date.getShortMonthName(i)), ++i); // get localised short month names
11186             return Ext.applyIf({
11187                 s:"(" + a.join("|") + ")"
11188             }, $f("F"));
11189         },
11190         m: {
11191             g:1,
11192             c:"m = parseInt(results[{0}], 10) - 1;\n",
11193             s:"(\\d{2})" // month number with leading zeros (01 - 12)
11194         },
11195         n: {
11196             g:1,
11197             c:"m = parseInt(results[{0}], 10) - 1;\n",
11198             s:"(\\d{1,2})" // month number without leading zeros (1 - 12)
11199         },
11200         t: {
11201             g:0,
11202             c:null,
11203             s:"(?:\\d{2})" // no. of days in the month (28 - 31)
11204         },
11205         L: {
11206             g:0,
11207             c:null,
11208             s:"(?:1|0)"
11209         },
11210         o: function() {
11211             return $f("Y");
11212         },
11213         Y: {
11214             g:1,
11215             c:"y = parseInt(results[{0}], 10);\n",
11216             s:"(\\d{4})" // 4-digit year
11217         },
11218         y: {
11219             g:1,
11220             c:"var ty = parseInt(results[{0}], 10);\n"
11221                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n", // 2-digit year
11222             s:"(\\d{1,2})"
11223         },
11224         a: {
11225             g:1,
11226             c:"if (results[{0}] == 'am') {\n"
11227                 + "if (!h || h == 12) { h = 0; }\n"
11228                 + "} else { if (!h || h < 12) { h = (h || 0) + 12; }}",
11229             s:"(am|pm)"
11230         },
11231         A: {
11232             g:1,
11233             c:"if (results[{0}] == 'AM') {\n"
11234                 + "if (!h || h == 12) { h = 0; }\n"
11235                 + "} else { if (!h || h < 12) { h = (h || 0) + 12; }}",
11236             s:"(AM|PM)"
11237         },
11238         g: function() {
11239             return $f("G");
11240         },
11241         G: {
11242             g:1,
11243             c:"h = parseInt(results[{0}], 10);\n",
11244             s:"(\\d{1,2})" // 24-hr format of an hour without leading zeroes (0 - 23)
11245         },
11246         h: function() {
11247             return $f("H");
11248         },
11249         H: {
11250             g:1,
11251             c:"h = parseInt(results[{0}], 10);\n",
11252             s:"(\\d{2})" //  24-hr format of an hour with leading zeroes (00 - 23)
11253         },
11254         i: {
11255             g:1,
11256             c:"i = parseInt(results[{0}], 10);\n",
11257             s:"(\\d{2})" // minutes with leading zeros (00 - 59)
11258         },
11259         s: {
11260             g:1,
11261             c:"s = parseInt(results[{0}], 10);\n",
11262             s:"(\\d{2})" // seconds with leading zeros (00 - 59)
11263         },
11264         u: {
11265             g:1,
11266             c:"ms = results[{0}]; ms = parseInt(ms, 10)/Math.pow(10, ms.length - 3);\n",
11267             s:"(\\d+)" // decimal fraction of a second (minimum = 1 digit, maximum = unlimited)
11268         },
11269         O: {
11270             g:1,
11271             c:[
11272                 "o = results[{0}];",
11273                 "var sn = o.substring(0,1),", // get + / - sign
11274                     "hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60),", // get hours (performs minutes-to-hour conversion also, just in case)
11275                     "mn = o.substring(3,5) % 60;", // get minutes
11276                 "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
11277             ].join("\n"),
11278             s: "([+\-]\\d{4})" // GMT offset in hrs and mins
11279         },
11280         P: {
11281             g:1,
11282             c:[
11283                 "o = results[{0}];",
11284                 "var sn = o.substring(0,1),", // get + / - sign
11285                     "hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60),", // get hours (performs minutes-to-hour conversion also, just in case)
11286                     "mn = o.substring(4,6) % 60;", // get minutes
11287                 "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
11288             ].join("\n"),
11289             s: "([+\-]\\d{2}:\\d{2})" // GMT offset in hrs and mins (with colon separator)
11290         },
11291         T: {
11292             g:0,
11293             c:null,
11294             s:"[A-Z]{1,4}" // timezone abbrev. may be between 1 - 4 chars
11295         },
11296         Z: {
11297             g:1,
11298             c:"zz = results[{0}] * 1;\n" // -43200 <= UTC offset <= 50400
11299                   + "zz = (-43200 <= zz && zz <= 50400)? zz : null;\n",
11300             s:"([+\-]?\\d{1,5})" // leading '+' sign is optional for UTC offset
11301         },
11302         c: function() {
11303             var calc = [],
11304                 arr = [
11305                     $f("Y", 1), // year
11306                     $f("m", 2), // month
11307                     $f("d", 3), // day
11308                     $f("h", 4), // hour
11309                     $f("i", 5), // minute
11310                     $f("s", 6), // second
11311                     {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)
11312                     {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
11313                         "if(results[8]) {", // timezone specified
11314                             "if(results[8] == 'Z'){",
11315                                 "zz = 0;", // UTC
11316                             "}else if (results[8].indexOf(':') > -1){",
11317                                 $f("P", 8).c, // timezone offset with colon separator
11318                             "}else{",
11319                                 $f("O", 8).c, // timezone offset without colon separator
11320                             "}",
11321                         "}"
11322                     ].join('\n')}
11323                 ];
11324
11325             for (var i = 0, l = arr.length; i < l; ++i) {
11326                 calc.push(arr[i].c);
11327             }
11328
11329             return {
11330                 g:1,
11331                 c:calc.join(""),
11332                 s:[
11333                     arr[0].s, // year (required)
11334                     "(?:", "-", arr[1].s, // month (optional)
11335                         "(?:", "-", arr[2].s, // day (optional)
11336                             "(?:",
11337                                 "(?:T| )?", // time delimiter -- either a "T" or a single blank space
11338                                 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
11339                                 "(?::", arr[5].s, ")?", // seconds (optional)
11340                                 "(?:(?:\\.|,)(\\d+))?", // decimal fraction of a second (e.g. ",12345" or ".98765") (optional)
11341                                 "(Z|(?:[-+]\\d{2}(?::)?\\d{2}))?", // "Z" (UTC) or "-0530" (UTC offset without colon delimiter) or "+08:00" (UTC offset with colon delimiter) (optional)
11342                             ")?",
11343                         ")?",
11344                     ")?"
11345                 ].join("")
11346             }
11347         },
11348         U: {
11349             g:1,
11350             c:"u = parseInt(results[{0}], 10);\n",
11351             s:"(-?\\d+)" // leading minus sign indicates seconds before UNIX epoch
11352         }
11353     }
11354 });
11355
11356 }());
11357
11358 Ext.apply(Date.prototype, {
11359     // private
11360     dateFormat : function(format) {
11361         if (Date.formatFunctions[format] == null) {
11362             Date.createFormat(format);
11363         }
11364         return Date.formatFunctions[format].call(this);
11365     },
11366
11367     /**
11368      * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
11369      *
11370      * Note: The date string returned by the javascript Date object's toString() method varies
11371      * between browsers (e.g. FF vs IE) and system region settings (e.g. IE in Asia vs IE in America).
11372      * For a given date string e.g. "Thu Oct 25 2007 22:55:35 GMT+0800 (Malay Peninsula Standard Time)",
11373      * getTimezone() first tries to get the timezone abbreviation from between a pair of parentheses
11374      * (which may or may not be present), failing which it proceeds to get the timezone abbreviation
11375      * from the GMT offset portion of the date string.
11376      * @return {String} The abbreviated timezone name (e.g. 'CST', 'PDT', 'EDT', 'MPST' ...).
11377      */
11378     getTimezone : function() {
11379         // the following list shows the differences between date strings from different browsers on a WinXP SP2 machine from an Asian locale:
11380         //
11381         // Opera  : "Thu, 25 Oct 2007 22:53:45 GMT+0800" -- shortest (weirdest) date string of the lot
11382         // 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)
11383         // FF     : "Thu Oct 25 2007 22:55:35 GMT+0800 (Malay Peninsula Standard Time)" -- value in parentheses always gives the correct timezone
11384         // IE     : "Thu Oct 25 22:54:35 UTC+0800 2007" -- (Asian system setting) look for 3-4 letter timezone abbrev
11385         // IE     : "Thu Oct 25 17:06:37 PDT 2007" -- (American system setting) look for 3-4 letter timezone abbrev
11386         //
11387         // this crazy regex attempts to guess the correct timezone abbreviation despite these differences.
11388         // step 1: (?:\((.*)\) -- find timezone in parentheses
11389         // 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
11390         // step 3: remove all non uppercase characters found in step 1 and 2
11391         return this.toString().replace(/^.* (?:\((.*)\)|([A-Z]{1,4})(?:[\-+][0-9]{4})?(?: -?\d+)?)$/, "$1$2").replace(/[^A-Z]/g, "");
11392     },
11393
11394     /**
11395      * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
11396      * @param {Boolean} colon (optional) true to separate the hours and minutes with a colon (defaults to false).
11397      * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600').
11398      */
11399     getGMTOffset : function(colon) {
11400         return (this.getTimezoneOffset() > 0 ? "-" : "+")
11401             + String.leftPad(Math.floor(Math.abs(this.getTimezoneOffset()) / 60), 2, "0")
11402             + (colon ? ":" : "")
11403             + String.leftPad(Math.abs(this.getTimezoneOffset() % 60), 2, "0");
11404     },
11405
11406     /**
11407      * Get the numeric day number of the year, adjusted for leap year.
11408      * @return {Number} 0 to 364 (365 in leap years).
11409      */
11410     getDayOfYear: function() {
11411         var num = 0,
11412             d = this.clone(),
11413             m = this.getMonth(),
11414             i;
11415
11416         for (i = 0, d.setDate(1), d.setMonth(0); i < m; d.setMonth(++i)) {
11417             num += d.getDaysInMonth();
11418         }
11419         return num + this.getDate() - 1;
11420     },
11421
11422     /**
11423      * Get the numeric ISO-8601 week number of the year.
11424      * (equivalent to the format specifier 'W', but without a leading zero).
11425      * @return {Number} 1 to 53
11426      */
11427     getWeekOfYear : function() {
11428         // adapted from http://www.merlyn.demon.co.uk/weekcalc.htm
11429         var ms1d = 864e5, // milliseconds in a day
11430             ms7d = 7 * ms1d; // milliseconds in a week
11431
11432         return function() { // return a closure so constants get calculated only once
11433             var DC3 = Date.UTC(this.getFullYear(), this.getMonth(), this.getDate() + 3) / ms1d, // an Absolute Day Number
11434                 AWN = Math.floor(DC3 / 7), // an Absolute Week Number
11435                 Wyr = new Date(AWN * ms7d).getUTCFullYear();
11436
11437             return AWN - Math.floor(Date.UTC(Wyr, 0, 7) / ms7d) + 1;
11438         }
11439     }(),
11440
11441     /**
11442      * Checks if the current date falls within a leap year.
11443      * @return {Boolean} True if the current date falls within a leap year, false otherwise.
11444      */
11445     isLeapYear : function() {
11446         var year = this.getFullYear();
11447         return !!((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
11448     },
11449
11450     /**
11451      * Get the first day of the current month, adjusted for leap year.  The returned value
11452      * is the numeric day index within the week (0-6) which can be used in conjunction with
11453      * the {@link #monthNames} array to retrieve the textual day name.
11454      * Example:
11455      * <pre><code>
11456 var dt = new Date('1/10/2007');
11457 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
11458 </code></pre>
11459      * @return {Number} The day number (0-6).
11460      */
11461     getFirstDayOfMonth : function() {
11462         var day = (this.getDay() - (this.getDate() - 1)) % 7;
11463         return (day < 0) ? (day + 7) : day;
11464     },
11465
11466     /**
11467      * Get the last day of the current month, adjusted for leap year.  The returned value
11468      * is the numeric day index within the week (0-6) which can be used in conjunction with
11469      * the {@link #monthNames} array to retrieve the textual day name.
11470      * Example:
11471      * <pre><code>
11472 var dt = new Date('1/10/2007');
11473 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
11474 </code></pre>
11475      * @return {Number} The day number (0-6).
11476      */
11477     getLastDayOfMonth : function() {
11478         return this.getLastDateOfMonth().getDay();
11479     },
11480
11481
11482     /**
11483      * Get the date of the first day of the month in which this date resides.
11484      * @return {Date}
11485      */
11486     getFirstDateOfMonth : function() {
11487         return new Date(this.getFullYear(), this.getMonth(), 1);
11488     },
11489
11490     /**
11491      * Get the date of the last day of the month in which this date resides.
11492      * @return {Date}
11493      */
11494     getLastDateOfMonth : function() {
11495         return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
11496     },
11497
11498     /**
11499      * Get the number of days in the current month, adjusted for leap year.
11500      * @return {Number} The number of days in the month.
11501      */
11502     getDaysInMonth: function() {
11503         var daysInMonth = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
11504
11505         return function() { // return a closure for efficiency
11506             var m = this.getMonth();
11507
11508             return m == 1 && this.isLeapYear() ? 29 : daysInMonth[m];
11509         }
11510     }(),
11511
11512     /**
11513      * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
11514      * @return {String} 'st, 'nd', 'rd' or 'th'.
11515      */
11516     getSuffix : function() {
11517         switch (this.getDate()) {
11518             case 1:
11519             case 21:
11520             case 31:
11521                 return "st";
11522             case 2:
11523             case 22:
11524                 return "nd";
11525             case 3:
11526             case 23:
11527                 return "rd";
11528             default:
11529                 return "th";
11530         }
11531     },
11532
11533     /**
11534      * Creates and returns a new Date instance with the exact same date value as the called instance.
11535      * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
11536      * variable will also be changed.  When the intention is to create a new variable that will not
11537      * modify the original instance, you should create a clone.
11538      *
11539      * Example of correctly cloning a date:
11540      * <pre><code>
11541 //wrong way:
11542 var orig = new Date('10/1/2006');
11543 var copy = orig;
11544 copy.setDate(5);
11545 document.write(orig);  //returns 'Thu Oct 05 2006'!
11546
11547 //correct way:
11548 var orig = new Date('10/1/2006');
11549 var copy = orig.clone();
11550 copy.setDate(5);
11551 document.write(orig);  //returns 'Thu Oct 01 2006'
11552 </code></pre>
11553      * @return {Date} The new Date instance.
11554      */
11555     clone : function() {
11556         return new Date(this.getTime());
11557     },
11558
11559     /**
11560      * Checks if the current date is affected by Daylight Saving Time (DST).
11561      * @return {Boolean} True if the current date is affected by DST.
11562      */
11563     isDST : function() {
11564         // adapted from http://extjs.com/forum/showthread.php?p=247172#post247172
11565         // courtesy of @geoffrey.mcgill
11566         return new Date(this.getFullYear(), 0, 1).getTimezoneOffset() != this.getTimezoneOffset();
11567     },
11568
11569     /**
11570      * Attempts to clear all time information from this Date by setting the time to midnight of the same day,
11571      * automatically adjusting for Daylight Saving Time (DST) where applicable.
11572      * (note: DST timezone information for the browser's host operating system is assumed to be up-to-date)
11573      * @param {Boolean} clone true to create a clone of this date, clear the time and return it (defaults to false).
11574      * @return {Date} this or the clone.
11575      */
11576     clearTime : function(clone) {
11577         if (clone) {
11578             return this.clone().clearTime();
11579         }
11580
11581         // get current date before clearing time
11582         var d = this.getDate();
11583
11584         // clear time
11585         this.setHours(0);
11586         this.setMinutes(0);
11587         this.setSeconds(0);
11588         this.setMilliseconds(0);
11589
11590         if (this.getDate() != d) { // account for DST (i.e. day of month changed when setting hour = 0)
11591             // note: DST adjustments are assumed to occur in multiples of 1 hour (this is almost always the case)
11592             // refer to http://www.timeanddate.com/time/aboutdst.html for the (rare) exceptions to this rule
11593
11594             // increment hour until cloned date == current date
11595             for (var hr = 1, c = this.add(Date.HOUR, hr); c.getDate() != d; hr++, c = this.add(Date.HOUR, hr));
11596
11597             this.setDate(d);
11598             this.setHours(c.getHours());
11599         }
11600
11601         return this;
11602     },
11603
11604     /**
11605      * Provides a convenient method for performing basic date arithmetic. This method
11606      * does not modify the Date instance being called - it creates and returns
11607      * a new Date instance containing the resulting date value.
11608      *
11609      * Examples:
11610      * <pre><code>
11611 // Basic usage:
11612 var dt = new Date('10/29/2006').add(Date.DAY, 5);
11613 document.write(dt); //returns 'Fri Nov 03 2006 00:00:00'
11614
11615 // Negative values will be subtracted:
11616 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
11617 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
11618
11619 // You can even chain several calls together in one line:
11620 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
11621 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
11622 </code></pre>
11623      *
11624      * @param {String} interval A valid date interval enum value.
11625      * @param {Number} value The amount to add to the current date.
11626      * @return {Date} The new Date instance.
11627      */
11628     add : function(interval, value) {
11629         var d = this.clone();
11630         if (!interval || value === 0) return d;
11631
11632         switch(interval.toLowerCase()) {
11633             case Date.MILLI:
11634                 d.setMilliseconds(this.getMilliseconds() + value);
11635                 break;
11636             case Date.SECOND:
11637                 d.setSeconds(this.getSeconds() + value);
11638                 break;
11639             case Date.MINUTE:
11640                 d.setMinutes(this.getMinutes() + value);
11641                 break;
11642             case Date.HOUR:
11643                 d.setHours(this.getHours() + value);
11644                 break;
11645             case Date.DAY:
11646                 d.setDate(this.getDate() + value);
11647                 break;
11648             case Date.MONTH:
11649                 var day = this.getDate();
11650                 if (day > 28) {
11651                     day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
11652                 }
11653                 d.setDate(day);
11654                 d.setMonth(this.getMonth() + value);
11655                 break;
11656             case Date.YEAR:
11657                 d.setFullYear(this.getFullYear() + value);
11658                 break;
11659         }
11660         return d;
11661     },
11662
11663     /**
11664      * Checks if this date falls on or between the given start and end dates.
11665      * @param {Date} start Start date
11666      * @param {Date} end End date
11667      * @return {Boolean} true if this date falls on or between the given start and end dates.
11668      */
11669     between : function(start, end) {
11670         var t = this.getTime();
11671         return start.getTime() <= t && t <= end.getTime();
11672     }
11673 });
11674
11675
11676 /**
11677  * Formats a date given the supplied format string.
11678  * @param {String} format The format string.
11679  * @return {String} The formatted date.
11680  * @method format
11681  */
11682 Date.prototype.format = Date.prototype.dateFormat;
11683
11684
11685 // private
11686 if (Ext.isSafari && (navigator.userAgent.match(/WebKit\/(\d+)/)[1] || NaN) < 420) {
11687     Ext.apply(Date.prototype, {
11688         _xMonth : Date.prototype.setMonth,
11689         _xDate  : Date.prototype.setDate,
11690
11691         // Bug in Safari 1.3, 2.0 (WebKit build < 420)
11692         // Date.setMonth does not work consistently if iMonth is not 0-11
11693         setMonth : function(num) {
11694             if (num <= -1) {
11695                 var n = Math.ceil(-num),
11696                     back_year = Math.ceil(n / 12),
11697                     month = (n % 12) ? 12 - n % 12 : 0;
11698
11699                 this.setFullYear(this.getFullYear() - back_year);
11700
11701                 return this._xMonth(month);
11702             } else {
11703                 return this._xMonth(num);
11704             }
11705         },
11706
11707         // Bug in setDate() method (resolved in WebKit build 419.3, so to be safe we target Webkit builds < 420)
11708         // The parameter for Date.setDate() is converted to a signed byte integer in Safari
11709         // http://brianary.blogspot.com/2006/03/safari-date-bug.html
11710         setDate : function(d) {
11711             // use setTime() to workaround setDate() bug
11712             // subtract current day of month in milliseconds, then add desired day of month in milliseconds
11713             return this.setTime(this.getTime() - (this.getDate() - d) * 864e5);
11714         }
11715     });
11716 }
11717
11718
11719
11720 /* Some basic Date tests... (requires Firebug)
11721
11722 Date.parseDate('', 'c'); // call Date.parseDate() once to force computation of regex string so we can console.log() it
11723 console.log('Insane Regex for "c" format: %o', Date.parseCodes.c.s); // view the insane regex for the "c" format specifier
11724
11725 // standard tests
11726 console.group('Standard Date.parseDate() Tests');
11727     console.log('Date.parseDate("2009-01-05T11:38:56", "c")               = %o', Date.parseDate("2009-01-05T11:38:56", "c")); // assumes browser's timezone setting
11728     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
11729     console.log('Date.parseDate("2009-03-03T13:36:54,101000Z", "c")       = %o', Date.parseDate("2009-03-03T13:36:54,101000Z", "c")); // UTC
11730     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
11731     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
11732 console.groupEnd();
11733
11734 // ISO-8601 format as specified in http://www.w3.org/TR/NOTE-datetime
11735 // -- accepts ALL 6 levels of date-time granularity
11736 console.group('ISO-8601 Granularity Test (see http://www.w3.org/TR/NOTE-datetime)');
11737     console.log('Date.parseDate("1997", "c")                              = %o', Date.parseDate("1997", "c")); // YYYY (e.g. 1997)
11738     console.log('Date.parseDate("1997-07", "c")                           = %o', Date.parseDate("1997-07", "c")); // YYYY-MM (e.g. 1997-07)
11739     console.log('Date.parseDate("1997-07-16", "c")                        = %o', Date.parseDate("1997-07-16", "c")); // YYYY-MM-DD (e.g. 1997-07-16)
11740     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)
11741     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)
11742     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)
11743     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)
11744     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
11745 console.groupEnd();
11746
11747 //*/
11748 /**
11749  * @class Ext.util.MixedCollection
11750  * @extends Ext.util.Observable
11751  * A Collection class that maintains both numeric indexes and keys and exposes events.
11752  * @constructor
11753  * @param {Boolean} allowFunctions Specify <tt>true</tt> if the {@link #addAll}
11754  * function should add function references to the collection. Defaults to
11755  * <tt>false</tt>.
11756  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
11757  * and return the key value for that item.  This is used when available to look up the key on items that
11758  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
11759  * equivalent to providing an implementation for the {@link #getKey} method.
11760  */
11761 Ext.util.MixedCollection = function(allowFunctions, keyFn){
11762     this.items = [];
11763     this.map = {};
11764     this.keys = [];
11765     this.length = 0;
11766     this.addEvents(
11767         /**
11768          * @event clear
11769          * Fires when the collection is cleared.
11770          */
11771         'clear',
11772         /**
11773          * @event add
11774          * Fires when an item is added to the collection.
11775          * @param {Number} index The index at which the item was added.
11776          * @param {Object} o The item added.
11777          * @param {String} key The key associated with the added item.
11778          */
11779         'add',
11780         /**
11781          * @event replace
11782          * Fires when an item is replaced in the collection.
11783          * @param {String} key he key associated with the new added.
11784          * @param {Object} old The item being replaced.
11785          * @param {Object} new The new item.
11786          */
11787         'replace',
11788         /**
11789          * @event remove
11790          * Fires when an item is removed from the collection.
11791          * @param {Object} o The item being removed.
11792          * @param {String} key (optional) The key associated with the removed item.
11793          */
11794         'remove',
11795         'sort'
11796     );
11797     this.allowFunctions = allowFunctions === true;
11798     if(keyFn){
11799         this.getKey = keyFn;
11800     }
11801     Ext.util.MixedCollection.superclass.constructor.call(this);
11802 };
11803
11804 Ext.extend(Ext.util.MixedCollection, Ext.util.Observable, {
11805
11806     /**
11807      * @cfg {Boolean} allowFunctions Specify <tt>true</tt> if the {@link #addAll}
11808      * function should add function references to the collection. Defaults to
11809      * <tt>false</tt>.
11810      */
11811     allowFunctions : false,
11812
11813     /**
11814      * Adds an item to the collection. Fires the {@link #add} event when complete.
11815      * @param {String} key <p>The key to associate with the item, or the new item.</p>
11816      * <p>If a {@link #getKey} implementation was specified for this MixedCollection,
11817      * or if the key of the stored items is in a property called <tt><b>id</b></tt>,
11818      * the MixedCollection will be able to <i>derive</i> the key for the new item.
11819      * In this case just pass the new item in this parameter.</p>
11820      * @param {Object} o The item to add.
11821      * @return {Object} The item added.
11822      */
11823     add : function(key, o){
11824         if(arguments.length == 1){
11825             o = arguments[0];
11826             key = this.getKey(o);
11827         }
11828         if(typeof key != 'undefined' && key !== null){
11829             var old = this.map[key];
11830             if(typeof old != 'undefined'){
11831                 return this.replace(key, o);
11832             }
11833             this.map[key] = o;
11834         }
11835         this.length++;
11836         this.items.push(o);
11837         this.keys.push(key);
11838         this.fireEvent('add', this.length-1, o, key);
11839         return o;
11840     },
11841
11842     /**
11843       * MixedCollection has a generic way to fetch keys if you implement getKey.  The default implementation
11844       * simply returns <b><code>item.id</code></b> but you can provide your own implementation
11845       * to return a different value as in the following examples:<pre><code>
11846 // normal way
11847 var mc = new Ext.util.MixedCollection();
11848 mc.add(someEl.dom.id, someEl);
11849 mc.add(otherEl.dom.id, otherEl);
11850 //and so on
11851
11852 // using getKey
11853 var mc = new Ext.util.MixedCollection();
11854 mc.getKey = function(el){
11855    return el.dom.id;
11856 };
11857 mc.add(someEl);
11858 mc.add(otherEl);
11859
11860 // or via the constructor
11861 var mc = new Ext.util.MixedCollection(false, function(el){
11862    return el.dom.id;
11863 });
11864 mc.add(someEl);
11865 mc.add(otherEl);
11866      * </code></pre>
11867      * @param {Object} item The item for which to find the key.
11868      * @return {Object} The key for the passed item.
11869      */
11870     getKey : function(o){
11871          return o.id;
11872     },
11873
11874     /**
11875      * Replaces an item in the collection. Fires the {@link #replace} event when complete.
11876      * @param {String} key <p>The key associated with the item to replace, or the replacement item.</p>
11877      * <p>If you supplied a {@link #getKey} implementation for this MixedCollection, or if the key
11878      * of your stored items is in a property called <tt><b>id</b></tt>, then the MixedCollection
11879      * will be able to <i>derive</i> the key of the replacement item. If you want to replace an item
11880      * with one having the same key value, then just pass the replacement item in this parameter.</p>
11881      * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate
11882      * with that key.
11883      * @return {Object}  The new item.
11884      */
11885     replace : function(key, o){
11886         if(arguments.length == 1){
11887             o = arguments[0];
11888             key = this.getKey(o);
11889         }
11890         var old = this.map[key];
11891         if(typeof key == 'undefined' || key === null || typeof old == 'undefined'){
11892              return this.add(key, o);
11893         }
11894         var index = this.indexOfKey(key);
11895         this.items[index] = o;
11896         this.map[key] = o;
11897         this.fireEvent('replace', key, old, o);
11898         return o;
11899     },
11900
11901     /**
11902      * Adds all elements of an Array or an Object to the collection.
11903      * @param {Object/Array} objs An Object containing properties which will be added
11904      * to the collection, or an Array of values, each of which are added to the collection.
11905      * Functions references will be added to the collection if <code>{@link #allowFunctions}</code>
11906      * has been set to <tt>true</tt>.
11907      */
11908     addAll : function(objs){
11909         if(arguments.length > 1 || Ext.isArray(objs)){
11910             var args = arguments.length > 1 ? arguments : objs;
11911             for(var i = 0, len = args.length; i < len; i++){
11912                 this.add(args[i]);
11913             }
11914         }else{
11915             for(var key in objs){
11916                 if(this.allowFunctions || typeof objs[key] != 'function'){
11917                     this.add(key, objs[key]);
11918                 }
11919             }
11920         }
11921     },
11922
11923     /**
11924      * Executes the specified function once for every item in the collection, passing the following arguments:
11925      * <div class="mdetail-params"><ul>
11926      * <li><b>item</b> : Mixed<p class="sub-desc">The collection item</p></li>
11927      * <li><b>index</b> : Number<p class="sub-desc">The item's index</p></li>
11928      * <li><b>length</b> : Number<p class="sub-desc">The total number of items in the collection</p></li>
11929      * </ul></div>
11930      * The function should return a boolean value. Returning false from the function will stop the iteration.
11931      * @param {Function} fn The function to execute for each item.
11932      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the current item in the iteration.
11933      */
11934     each : function(fn, scope){
11935         var items = [].concat(this.items); // each safe for removal
11936         for(var i = 0, len = items.length; i < len; i++){
11937             if(fn.call(scope || items[i], items[i], i, len) === false){
11938                 break;
11939             }
11940         }
11941     },
11942
11943     /**
11944      * Executes the specified function once for every key in the collection, passing each
11945      * key, and its associated item as the first two parameters.
11946      * @param {Function} fn The function to execute for each item.
11947      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the browser window.
11948      */
11949     eachKey : function(fn, scope){
11950         for(var i = 0, len = this.keys.length; i < len; i++){
11951             fn.call(scope || window, this.keys[i], this.items[i], i, len);
11952         }
11953     },
11954
11955     /**
11956      * Returns the first item in the collection which elicits a true return value from the
11957      * passed selection function.
11958      * @param {Function} fn The selection function to execute for each item.
11959      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the browser window.
11960      * @return {Object} The first item in the collection which returned true from the selection function.
11961      */
11962     find : function(fn, scope){
11963         for(var i = 0, len = this.items.length; i < len; i++){
11964             if(fn.call(scope || window, this.items[i], this.keys[i])){
11965                 return this.items[i];
11966             }
11967         }
11968         return null;
11969     },
11970
11971     /**
11972      * Inserts an item at the specified index in the collection. Fires the {@link #add} event when complete.
11973      * @param {Number} index The index to insert the item at.
11974      * @param {String} key The key to associate with the new item, or the item itself.
11975      * @param {Object} o (optional) If the second parameter was a key, the new item.
11976      * @return {Object} The item inserted.
11977      */
11978     insert : function(index, key, o){
11979         if(arguments.length == 2){
11980             o = arguments[1];
11981             key = this.getKey(o);
11982         }
11983         if(this.containsKey(key)){
11984             this.suspendEvents();
11985             this.removeKey(key);
11986             this.resumeEvents();
11987         }
11988         if(index >= this.length){
11989             return this.add(key, o);
11990         }
11991         this.length++;
11992         this.items.splice(index, 0, o);
11993         if(typeof key != 'undefined' && key !== null){
11994             this.map[key] = o;
11995         }
11996         this.keys.splice(index, 0, key);
11997         this.fireEvent('add', index, o, key);
11998         return o;
11999     },
12000
12001     /**
12002      * Remove an item from the collection.
12003      * @param {Object} o The item to remove.
12004      * @return {Object} The item removed or false if no item was removed.
12005      */
12006     remove : function(o){
12007         return this.removeAt(this.indexOf(o));
12008     },
12009
12010     /**
12011      * Remove an item from a specified index in the collection. Fires the {@link #remove} event when complete.
12012      * @param {Number} index The index within the collection of the item to remove.
12013      * @return {Object} The item removed or false if no item was removed.
12014      */
12015     removeAt : function(index){
12016         if(index < this.length && index >= 0){
12017             this.length--;
12018             var o = this.items[index];
12019             this.items.splice(index, 1);
12020             var key = this.keys[index];
12021             if(typeof key != 'undefined'){
12022                 delete this.map[key];
12023             }
12024             this.keys.splice(index, 1);
12025             this.fireEvent('remove', o, key);
12026             return o;
12027         }
12028         return false;
12029     },
12030
12031     /**
12032      * Removed an item associated with the passed key fom the collection.
12033      * @param {String} key The key of the item to remove.
12034      * @return {Object} The item removed or false if no item was removed.
12035      */
12036     removeKey : function(key){
12037         return this.removeAt(this.indexOfKey(key));
12038     },
12039
12040     /**
12041      * Returns the number of items in the collection.
12042      * @return {Number} the number of items in the collection.
12043      */
12044     getCount : function(){
12045         return this.length;
12046     },
12047
12048     /**
12049      * Returns index within the collection of the passed Object.
12050      * @param {Object} o The item to find the index of.
12051      * @return {Number} index of the item. Returns -1 if not found.
12052      */
12053     indexOf : function(o){
12054         return this.items.indexOf(o);
12055     },
12056
12057     /**
12058      * Returns index within the collection of the passed key.
12059      * @param {String} key The key to find the index of.
12060      * @return {Number} index of the key.
12061      */
12062     indexOfKey : function(key){
12063         return this.keys.indexOf(key);
12064     },
12065
12066     /**
12067      * Returns the item associated with the passed key OR index.
12068      * Key has priority over index.  This is the equivalent
12069      * of calling {@link #key} first, then if nothing matched calling {@link #itemAt}.
12070      * @param {String/Number} key The key or index of the item.
12071      * @return {Object} If the item is found, returns the item.  If the item was not found, returns <tt>undefined</tt>.
12072      * If an item was found, but is a Class, returns <tt>null</tt>.
12073      */
12074     item : function(key){
12075         var mk = this.map[key],
12076             item = mk !== undefined ? mk : (typeof key == 'number') ? this.items[key] : undefined;
12077         return !Ext.isFunction(item) || this.allowFunctions ? item : null; // for prototype!
12078     },
12079
12080     /**
12081      * Returns the item at the specified index.
12082      * @param {Number} index The index of the item.
12083      * @return {Object} The item at the specified index.
12084      */
12085     itemAt : function(index){
12086         return this.items[index];
12087     },
12088
12089     /**
12090      * Returns the item associated with the passed key.
12091      * @param {String/Number} key The key of the item.
12092      * @return {Object} The item associated with the passed key.
12093      */
12094     key : function(key){
12095         return this.map[key];
12096     },
12097
12098     /**
12099      * Returns true if the collection contains the passed Object as an item.
12100      * @param {Object} o  The Object to look for in the collection.
12101      * @return {Boolean} True if the collection contains the Object as an item.
12102      */
12103     contains : function(o){
12104         return this.indexOf(o) != -1;
12105     },
12106
12107     /**
12108      * Returns true if the collection contains the passed Object as a key.
12109      * @param {String} key The key to look for in the collection.
12110      * @return {Boolean} True if the collection contains the Object as a key.
12111      */
12112     containsKey : function(key){
12113         return typeof this.map[key] != 'undefined';
12114     },
12115
12116     /**
12117      * Removes all items from the collection.  Fires the {@link #clear} event when complete.
12118      */
12119     clear : function(){
12120         this.length = 0;
12121         this.items = [];
12122         this.keys = [];
12123         this.map = {};
12124         this.fireEvent('clear');
12125     },
12126
12127     /**
12128      * Returns the first item in the collection.
12129      * @return {Object} the first item in the collection..
12130      */
12131     first : function(){
12132         return this.items[0];
12133     },
12134
12135     /**
12136      * Returns the last item in the collection.
12137      * @return {Object} the last item in the collection..
12138      */
12139     last : function(){
12140         return this.items[this.length-1];
12141     },
12142
12143     /**
12144      * @private
12145      * Performs the actual sorting based on a direction and a sorting function. Internally,
12146      * this creates a temporary array of all items in the MixedCollection, sorts it and then writes
12147      * the sorted array data back into this.items and this.keys
12148      * @param {String} property Property to sort by ('key', 'value', or 'index')
12149      * @param {String} dir (optional) Direction to sort 'ASC' or 'DESC'. Defaults to 'ASC'.
12150      * @param {Function} fn (optional) Comparison function that defines the sort order.
12151      * Defaults to sorting by numeric value.
12152      */
12153     _sort : function(property, dir, fn){
12154         var i, len,
12155             dsc   = String(dir).toUpperCase() == 'DESC' ? -1 : 1,
12156             
12157             //this is a temporary array used to apply the sorting function
12158             c     = [],
12159             keys  = this.keys,
12160             items = this.items;
12161         
12162         //default to a simple sorter function if one is not provided
12163         fn = fn || function(a, b) {
12164             return a - b;
12165         };
12166         
12167         //copy all the items into a temporary array, which we will sort
12168         for(i = 0, len = items.length; i < len; i++){
12169             c[c.length] = {
12170                 key  : keys[i], 
12171                 value: items[i], 
12172                 index: i
12173             };
12174         }
12175         
12176         //sort the temporary array
12177         c.sort(function(a, b){
12178             var v = fn(a[property], b[property]) * dsc;
12179             if(v === 0){
12180                 v = (a.index < b.index ? -1 : 1);
12181             }
12182             return v;
12183         });
12184         
12185         //copy the temporary array back into the main this.items and this.keys objects
12186         for(i = 0, len = c.length; i < len; i++){
12187             items[i] = c[i].value;
12188             keys[i]  = c[i].key;
12189         }
12190         
12191         this.fireEvent('sort', this);
12192     },
12193
12194     /**
12195      * Sorts this collection by <b>item</b> value with the passed comparison function.
12196      * @param {String} direction (optional) 'ASC' or 'DESC'. Defaults to 'ASC'.
12197      * @param {Function} fn (optional) Comparison function that defines the sort order.
12198      * Defaults to sorting by numeric value.
12199      */
12200     sort : function(dir, fn){
12201         this._sort('value', dir, fn);
12202     },
12203     
12204     /**
12205      * Reorders each of the items based on a mapping from old index to new index. Internally this
12206      * just translates into a sort. The 'sort' event is fired whenever reordering has occured.
12207      * @param {Object} mapping Mapping from old item index to new item index
12208      */
12209     reorder: function(mapping) {
12210         this.suspendEvents();
12211         
12212         var items     = this.items,
12213             index     = 0,
12214             length    = items.length,
12215             order     = [],
12216             remaining = [];
12217         
12218         //object of {oldPosition: newPosition} reversed to {newPosition: oldPosition}
12219         for (oldIndex in mapping) {
12220             order[mapping[oldIndex]] = items[oldIndex];
12221         } 
12222         
12223         for (index = 0; index < length; index++) {
12224             if (mapping[index] == undefined) {
12225                 remaining.push(items[index]);
12226             }
12227         }
12228         
12229         for (index = 0; index < length; index++) {
12230             if (order[index] == undefined) {
12231                 order[index] = remaining.shift();
12232             }
12233         }
12234         
12235         this.clear();
12236         this.addAll(order);
12237         
12238         this.resumeEvents();
12239         this.fireEvent('sort', this);
12240     },
12241
12242     /**
12243      * Sorts this collection by <b>key</b>s.
12244      * @param {String} direction (optional) 'ASC' or 'DESC'. Defaults to 'ASC'.
12245      * @param {Function} fn (optional) Comparison function that defines the sort order.
12246      * Defaults to sorting by case insensitive string.
12247      */
12248     keySort : function(dir, fn){
12249         this._sort('key', dir, fn || function(a, b){
12250             var v1 = String(a).toUpperCase(), v2 = String(b).toUpperCase();
12251             return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
12252         });
12253     },
12254
12255     /**
12256      * Returns a range of items in this collection
12257      * @param {Number} startIndex (optional) The starting index. Defaults to 0.
12258      * @param {Number} endIndex (optional) The ending index. Defaults to the last item.
12259      * @return {Array} An array of items
12260      */
12261     getRange : function(start, end){
12262         var items = this.items;
12263         if(items.length < 1){
12264             return [];
12265         }
12266         start = start || 0;
12267         end = Math.min(typeof end == 'undefined' ? this.length-1 : end, this.length-1);
12268         var i, r = [];
12269         if(start <= end){
12270             for(i = start; i <= end; i++) {
12271                 r[r.length] = items[i];
12272             }
12273         }else{
12274             for(i = start; i >= end; i--) {
12275                 r[r.length] = items[i];
12276             }
12277         }
12278         return r;
12279     },
12280
12281     /**
12282      * Filter the <i>objects</i> in this collection by a specific property.
12283      * Returns a new collection that has been filtered.
12284      * @param {String} property A property on your objects
12285      * @param {String/RegExp} value Either string that the property values
12286      * should start with or a RegExp to test against the property
12287      * @param {Boolean} anyMatch (optional) True to match any part of the string, not just the beginning
12288      * @param {Boolean} caseSensitive (optional) True for case sensitive comparison (defaults to False).
12289      * @return {MixedCollection} The new filtered collection
12290      */
12291     filter : function(property, value, anyMatch, caseSensitive){
12292         if(Ext.isEmpty(value, false)){
12293             return this.clone();
12294         }
12295         value = this.createValueMatcher(value, anyMatch, caseSensitive);
12296         return this.filterBy(function(o){
12297             return o && value.test(o[property]);
12298         });
12299     },
12300
12301     /**
12302      * Filter by a function. Returns a <i>new</i> collection that has been filtered.
12303      * The passed function will be called with each object in the collection.
12304      * If the function returns true, the value is included otherwise it is filtered.
12305      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
12306      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to this MixedCollection.
12307      * @return {MixedCollection} The new filtered collection
12308      */
12309     filterBy : function(fn, scope){
12310         var r = new Ext.util.MixedCollection();
12311         r.getKey = this.getKey;
12312         var k = this.keys, it = this.items;
12313         for(var i = 0, len = it.length; i < len; i++){
12314             if(fn.call(scope||this, it[i], k[i])){
12315                 r.add(k[i], it[i]);
12316             }
12317         }
12318         return r;
12319     },
12320
12321     /**
12322      * Finds the index of the first matching object in this collection by a specific property/value.
12323      * @param {String} property The name of a property on your objects.
12324      * @param {String/RegExp} value A string that the property values
12325      * should start with or a RegExp to test against the property.
12326      * @param {Number} start (optional) The index to start searching at (defaults to 0).
12327      * @param {Boolean} anyMatch (optional) True to match any part of the string, not just the beginning.
12328      * @param {Boolean} caseSensitive (optional) True for case sensitive comparison.
12329      * @return {Number} The matched index or -1
12330      */
12331     findIndex : function(property, value, start, anyMatch, caseSensitive){
12332         if(Ext.isEmpty(value, false)){
12333             return -1;
12334         }
12335         value = this.createValueMatcher(value, anyMatch, caseSensitive);
12336         return this.findIndexBy(function(o){
12337             return o && value.test(o[property]);
12338         }, null, start);
12339     },
12340
12341     /**
12342      * Find the index of the first matching object in this collection by a function.
12343      * If the function returns <i>true</i> it is considered a match.
12344      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key).
12345      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to this MixedCollection.
12346      * @param {Number} start (optional) The index to start searching at (defaults to 0).
12347      * @return {Number} The matched index or -1
12348      */
12349     findIndexBy : function(fn, scope, start){
12350         var k = this.keys, it = this.items;
12351         for(var i = (start||0), len = it.length; i < len; i++){
12352             if(fn.call(scope||this, it[i], k[i])){
12353                 return i;
12354             }
12355         }
12356         return -1;
12357     },
12358
12359     /**
12360      * Returns a regular expression based on the given value and matching options. This is used internally for finding and filtering,
12361      * and by Ext.data.Store#filter
12362      * @private
12363      * @param {String} value The value to create the regex for. This is escaped using Ext.escapeRe
12364      * @param {Boolean} anyMatch True to allow any match - no regex start/end line anchors will be added. Defaults to false
12365      * @param {Boolean} caseSensitive True to make the regex case sensitive (adds 'i' switch to regex). Defaults to false.
12366      * @param {Boolean} exactMatch True to force exact match (^ and $ characters added to the regex). Defaults to false. Ignored if anyMatch is true.
12367      */
12368     createValueMatcher : function(value, anyMatch, caseSensitive, exactMatch) {
12369         if (!value.exec) { // not a regex
12370             var er = Ext.escapeRe;
12371             value = String(value);
12372             
12373             if (anyMatch === true) {
12374                 value = er(value);
12375             } else {
12376                 value = '^' + er(value);
12377                 if (exactMatch === true) {
12378                     value += '$';
12379                 }
12380             }
12381             value = new RegExp(value, caseSensitive ? '' : 'i');
12382          }
12383          return value;
12384     },
12385
12386     /**
12387      * Creates a shallow copy of this collection
12388      * @return {MixedCollection}
12389      */
12390     clone : function(){
12391         var r = new Ext.util.MixedCollection();
12392         var k = this.keys, it = this.items;
12393         for(var i = 0, len = it.length; i < len; i++){
12394             r.add(k[i], it[i]);
12395         }
12396         r.getKey = this.getKey;
12397         return r;
12398     }
12399 });
12400 /**
12401  * This method calls {@link #item item()}.
12402  * Returns the item associated with the passed key OR index. Key has priority
12403  * over index.  This is the equivalent of calling {@link #key} first, then if
12404  * nothing matched calling {@link #itemAt}.
12405  * @param {String/Number} key The key or index of the item.
12406  * @return {Object} If the item is found, returns the item.  If the item was
12407  * not found, returns <tt>undefined</tt>. If an item was found, but is a Class,
12408  * returns <tt>null</tt>.
12409  */
12410 Ext.util.MixedCollection.prototype.get = Ext.util.MixedCollection.prototype.item;/**
12411  * @class Ext.util.JSON
12412  * Modified version of Douglas Crockford"s json.js that doesn"t
12413  * mess with the Object prototype
12414  * http://www.json.org/js.html
12415  * @singleton
12416  */
12417 Ext.util.JSON = new (function(){
12418     var useHasOwn = !!{}.hasOwnProperty,
12419         isNative = function() {
12420             var useNative = null;
12421
12422             return function() {
12423                 if (useNative === null) {
12424                     useNative = Ext.USE_NATIVE_JSON && window.JSON && JSON.toString() == '[object JSON]';
12425                 }
12426         
12427                 return useNative;
12428             };
12429         }(),
12430         pad = function(n) {
12431             return n < 10 ? "0" + n : n;
12432         },
12433         doDecode = function(json){
12434             return eval("(" + json + ')');    
12435         },
12436         doEncode = function(o){
12437             if(!Ext.isDefined(o) || o === null){
12438                 return "null";
12439             }else if(Ext.isArray(o)){
12440                 return encodeArray(o);
12441             }else if(Ext.isDate(o)){
12442                 return Ext.util.JSON.encodeDate(o);
12443             }else if(Ext.isString(o)){
12444                 return encodeString(o);
12445             }else if(typeof o == "number"){
12446                 //don't use isNumber here, since finite checks happen inside isNumber
12447                 return isFinite(o) ? String(o) : "null";
12448             }else if(Ext.isBoolean(o)){
12449                 return String(o);
12450             }else {
12451                 var a = ["{"], b, i, v;
12452                 for (i in o) {
12453                     // don't encode DOM objects
12454                     if(!o.getElementsByTagName){
12455                         if(!useHasOwn || o.hasOwnProperty(i)) {
12456                             v = o[i];
12457                             switch (typeof v) {
12458                             case "undefined":
12459                             case "function":
12460                             case "unknown":
12461                                 break;
12462                             default:
12463                                 if(b){
12464                                     a.push(',');
12465                                 }
12466                                 a.push(doEncode(i), ":",
12467                                         v === null ? "null" : doEncode(v));
12468                                 b = true;
12469                             }
12470                         }
12471                     }
12472                 }
12473                 a.push("}");
12474                 return a.join("");
12475             }    
12476         },
12477         m = {
12478             "\b": '\\b',
12479             "\t": '\\t',
12480             "\n": '\\n',
12481             "\f": '\\f',
12482             "\r": '\\r',
12483             '"' : '\\"',
12484             "\\": '\\\\'
12485         },
12486         encodeString = function(s){
12487             if (/["\\\x00-\x1f]/.test(s)) {
12488                 return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
12489                     var c = m[b];
12490                     if(c){
12491                         return c;
12492                     }
12493                     c = b.charCodeAt();
12494                     return "\\u00" +
12495                         Math.floor(c / 16).toString(16) +
12496                         (c % 16).toString(16);
12497                 }) + '"';
12498             }
12499             return '"' + s + '"';
12500         },
12501         encodeArray = function(o){
12502             var a = ["["], b, i, l = o.length, v;
12503                 for (i = 0; i < l; i += 1) {
12504                     v = o[i];
12505                     switch (typeof v) {
12506                         case "undefined":
12507                         case "function":
12508                         case "unknown":
12509                             break;
12510                         default:
12511                             if (b) {
12512                                 a.push(',');
12513                             }
12514                             a.push(v === null ? "null" : Ext.util.JSON.encode(v));
12515                             b = true;
12516                     }
12517                 }
12518                 a.push("]");
12519                 return a.join("");
12520         };
12521
12522     /**
12523      * <p>Encodes a Date. This returns the actual string which is inserted into the JSON string as the literal expression.
12524      * <b>The returned value includes enclosing double quotation marks.</b></p>
12525      * <p>The default return format is "yyyy-mm-ddThh:mm:ss".</p>
12526      * <p>To override this:</p><pre><code>
12527 Ext.util.JSON.encodeDate = function(d) {
12528     return d.format('"Y-m-d"');
12529 };
12530 </code></pre>
12531      * @param {Date} d The Date to encode
12532      * @return {String} The string literal to use in a JSON string.
12533      */
12534     this.encodeDate = function(o){
12535         return '"' + o.getFullYear() + "-" +
12536                 pad(o.getMonth() + 1) + "-" +
12537                 pad(o.getDate()) + "T" +
12538                 pad(o.getHours()) + ":" +
12539                 pad(o.getMinutes()) + ":" +
12540                 pad(o.getSeconds()) + '"';
12541     };
12542
12543     /**
12544      * Encodes an Object, Array or other value
12545      * @param {Mixed} o The variable to encode
12546      * @return {String} The JSON string
12547      */
12548     this.encode = function() {
12549         var ec;
12550         return function(o) {
12551             if (!ec) {
12552                 // setup encoding function on first access
12553                 ec = isNative() ? JSON.stringify : doEncode;
12554             }
12555             return ec(o);
12556         };
12557     }();
12558
12559
12560     /**
12561      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError unless the safe option is set.
12562      * @param {String} json The JSON string
12563      * @return {Object} The resulting object
12564      */
12565     this.decode = function() {
12566         var dc;
12567         return function(json) {
12568             if (!dc) {
12569                 // setup decoding function on first access
12570                 dc = isNative() ? JSON.parse : doDecode;
12571             }
12572             return dc(json);
12573         };
12574     }();
12575
12576 })();
12577 /**
12578  * Shorthand for {@link Ext.util.JSON#encode}
12579  * @param {Mixed} o The variable to encode
12580  * @return {String} The JSON string
12581  * @member Ext
12582  * @method encode
12583  */
12584 Ext.encode = Ext.util.JSON.encode;
12585 /**
12586  * Shorthand for {@link Ext.util.JSON#decode}
12587  * @param {String} json The JSON string
12588  * @param {Boolean} safe (optional) Whether to return null or throw an exception if the JSON is invalid.
12589  * @return {Object} The resulting object
12590  * @member Ext
12591  * @method decode
12592  */
12593 Ext.decode = Ext.util.JSON.decode;
12594 /**
12595  * @class Ext.util.Format
12596  * Reusable data formatting functions
12597  * @singleton
12598  */
12599 Ext.util.Format = function(){
12600     var trimRe = /^\s+|\s+$/g,
12601         stripTagsRE = /<\/?[^>]+>/gi,
12602         stripScriptsRe = /(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig,
12603         nl2brRe = /\r?\n/g;
12604
12605     return {
12606         /**
12607          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
12608          * @param {String} value The string to truncate
12609          * @param {Number} length The maximum length to allow before truncating
12610          * @param {Boolean} word True to try to find a common work break
12611          * @return {String} The converted text
12612          */
12613         ellipsis : function(value, len, word){
12614             if(value && value.length > len){
12615                 if(word){
12616                     var vs = value.substr(0, len - 2),
12617                         index = Math.max(vs.lastIndexOf(' '), vs.lastIndexOf('.'), vs.lastIndexOf('!'), vs.lastIndexOf('?'));
12618                     if(index == -1 || index < (len - 15)){
12619                         return value.substr(0, len - 3) + "...";
12620                     }else{
12621                         return vs.substr(0, index) + "...";
12622                     }
12623                 } else{
12624                     return value.substr(0, len - 3) + "...";
12625                 }
12626             }
12627             return value;
12628         },
12629
12630         /**
12631          * Checks a reference and converts it to empty string if it is undefined
12632          * @param {Mixed} value Reference to check
12633          * @return {Mixed} Empty string if converted, otherwise the original value
12634          */
12635         undef : function(value){
12636             return value !== undefined ? value : "";
12637         },
12638
12639         /**
12640          * Checks a reference and converts it to the default value if it's empty
12641          * @param {Mixed} value Reference to check
12642          * @param {String} defaultValue The value to insert of it's undefined (defaults to "")
12643          * @return {String}
12644          */
12645         defaultValue : function(value, defaultValue){
12646             return value !== undefined && value !== '' ? value : defaultValue;
12647         },
12648
12649         /**
12650          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
12651          * @param {String} value The string to encode
12652          * @return {String} The encoded text
12653          */
12654         htmlEncode : function(value){
12655             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
12656         },
12657
12658         /**
12659          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
12660          * @param {String} value The string to decode
12661          * @return {String} The decoded text
12662          */
12663         htmlDecode : function(value){
12664             return !value ? value : String(value).replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"').replace(/&amp;/g, "&");
12665         },
12666
12667         /**
12668          * Trims any whitespace from either side of a string
12669          * @param {String} value The text to trim
12670          * @return {String} The trimmed text
12671          */
12672         trim : function(value){
12673             return String(value).replace(trimRe, "");
12674         },
12675
12676         /**
12677          * Returns a substring from within an original string
12678          * @param {String} value The original text
12679          * @param {Number} start The start index of the substring
12680          * @param {Number} length The length of the substring
12681          * @return {String} The substring
12682          */
12683         substr : function(value, start, length){
12684             return String(value).substr(start, length);
12685         },
12686
12687         /**
12688          * Converts a string to all lower case letters
12689          * @param {String} value The text to convert
12690          * @return {String} The converted text
12691          */
12692         lowercase : function(value){
12693             return String(value).toLowerCase();
12694         },
12695
12696         /**
12697          * Converts a string to all upper case letters
12698          * @param {String} value The text to convert
12699          * @return {String} The converted text
12700          */
12701         uppercase : function(value){
12702             return String(value).toUpperCase();
12703         },
12704
12705         /**
12706          * Converts the first character only of a string to upper case
12707          * @param {String} value The text to convert
12708          * @return {String} The converted text
12709          */
12710         capitalize : function(value){
12711             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
12712         },
12713
12714         // private
12715         call : function(value, fn){
12716             if(arguments.length > 2){
12717                 var args = Array.prototype.slice.call(arguments, 2);
12718                 args.unshift(value);
12719                 return eval(fn).apply(window, args);
12720             }else{
12721                 return eval(fn).call(window, value);
12722             }
12723         },
12724
12725         /**
12726          * Format a number as US currency
12727          * @param {Number/String} value The numeric value to format
12728          * @return {String} The formatted currency string
12729          */
12730         usMoney : function(v){
12731             v = (Math.round((v-0)*100))/100;
12732             v = (v == Math.floor(v)) ? v + ".00" : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
12733             v = String(v);
12734             var ps = v.split('.'),
12735                 whole = ps[0],
12736                 sub = ps[1] ? '.'+ ps[1] : '.00',
12737                 r = /(\d+)(\d{3})/;
12738             while (r.test(whole)) {
12739                 whole = whole.replace(r, '$1' + ',' + '$2');
12740             }
12741             v = whole + sub;
12742             if(v.charAt(0) == '-'){
12743                 return '-$' + v.substr(1);
12744             }
12745             return "$" +  v;
12746         },
12747
12748         /**
12749          * Parse a value into a formatted date using the specified format pattern.
12750          * @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)
12751          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
12752          * @return {String} The formatted date string
12753          */
12754         date : function(v, format){
12755             if(!v){
12756                 return "";
12757             }
12758             if(!Ext.isDate(v)){
12759                 v = new Date(Date.parse(v));
12760             }
12761             return v.dateFormat(format || "m/d/Y");
12762         },
12763
12764         /**
12765          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
12766          * @param {String} format Any valid date format string
12767          * @return {Function} The date formatting function
12768          */
12769         dateRenderer : function(format){
12770             return function(v){
12771                 return Ext.util.Format.date(v, format);
12772             };
12773         },
12774
12775         /**
12776          * Strips all HTML tags
12777          * @param {Mixed} value The text from which to strip tags
12778          * @return {String} The stripped text
12779          */
12780         stripTags : function(v){
12781             return !v ? v : String(v).replace(stripTagsRE, "");
12782         },
12783
12784         /**
12785          * Strips all script tags
12786          * @param {Mixed} value The text from which to strip script tags
12787          * @return {String} The stripped text
12788          */
12789         stripScripts : function(v){
12790             return !v ? v : String(v).replace(stripScriptsRe, "");
12791         },
12792
12793         /**
12794          * Simple format for a file size (xxx bytes, xxx KB, xxx MB)
12795          * @param {Number/String} size The numeric value to format
12796          * @return {String} The formatted file size
12797          */
12798         fileSize : function(size){
12799             if(size < 1024) {
12800                 return size + " bytes";
12801             } else if(size < 1048576) {
12802                 return (Math.round(((size*10) / 1024))/10) + " KB";
12803             } else {
12804                 return (Math.round(((size*10) / 1048576))/10) + " MB";
12805             }
12806         },
12807
12808         /**
12809          * It does simple math for use in a template, for example:<pre><code>
12810          * var tpl = new Ext.Template('{value} * 10 = {value:math("* 10")}');
12811          * </code></pre>
12812          * @return {Function} A function that operates on the passed value.
12813          */
12814         math : function(){
12815             var fns = {};
12816             return function(v, a){
12817                 if(!fns[a]){
12818                     fns[a] = new Function('v', 'return v ' + a + ';');
12819                 }
12820                 return fns[a](v);
12821             }
12822         }(),
12823
12824         /**
12825          * Rounds the passed number to the required decimal precision.
12826          * @param {Number/String} value The numeric value to round.
12827          * @param {Number} precision The number of decimal places to which to round the first parameter's value.
12828          * @return {Number} The rounded value.
12829          */
12830         round : function(value, precision) {
12831             var result = Number(value);
12832             if (typeof precision == 'number') {
12833                 precision = Math.pow(10, precision);
12834                 result = Math.round(value * precision) / precision;
12835             }
12836             return result;
12837         },
12838
12839         /**
12840          * Formats the number according to the format string.
12841          * <div style="margin-left:40px">examples (123456.789):
12842          * <div style="margin-left:10px">
12843          * 0 - (123456) show only digits, no precision<br>
12844          * 0.00 - (123456.78) show only digits, 2 precision<br>
12845          * 0.0000 - (123456.7890) show only digits, 4 precision<br>
12846          * 0,000 - (123,456) show comma and digits, no precision<br>
12847          * 0,000.00 - (123,456.78) show comma and digits, 2 precision<br>
12848          * 0,0.00 - (123,456.78) shortcut method, show comma and digits, 2 precision<br>
12849          * To reverse the grouping (,) and decimal (.) for international numbers, add /i to the end.
12850          * For example: 0.000,00/i
12851          * </div></div>
12852          * @param {Number} v The number to format.
12853          * @param {String} format The way you would like to format this text.
12854          * @return {String} The formatted number.
12855          */
12856         number: function(v, format) {
12857             if(!format){
12858                 return v;
12859             }
12860             v = Ext.num(v, NaN);
12861             if (isNaN(v)){
12862                 return '';
12863             }
12864             var comma = ',',
12865                 dec = '.',
12866                 i18n = false,
12867                 neg = v < 0;
12868
12869             v = Math.abs(v);
12870             if(format.substr(format.length - 2) == '/i'){
12871                 format = format.substr(0, format.length - 2);
12872                 i18n = true;
12873                 comma = '.';
12874                 dec = ',';
12875             }
12876
12877             var hasComma = format.indexOf(comma) != -1,
12878                 psplit = (i18n ? format.replace(/[^\d\,]/g, '') : format.replace(/[^\d\.]/g, '')).split(dec);
12879
12880             if(1 < psplit.length){
12881                 v = v.toFixed(psplit[1].length);
12882             }else if(2 < psplit.length){
12883                 throw ('NumberFormatException: invalid format, formats should have no more than 1 period: ' + format);
12884             }else{
12885                 v = v.toFixed(0);
12886             }
12887
12888             var fnum = v.toString();
12889
12890             psplit = fnum.split('.');
12891
12892             if (hasComma) {
12893                 var cnum = psplit[0], parr = [], j = cnum.length, m = Math.floor(j / 3), n = cnum.length % 3 || 3;
12894
12895                 for (var i = 0; i < j; i += n) {
12896                     if (i != 0) {
12897                         n = 3;
12898                     }
12899                     parr[parr.length] = cnum.substr(i, n);
12900                     m -= 1;
12901                 }
12902                 fnum = parr.join(comma);
12903                 if (psplit[1]) {
12904                     fnum += dec + psplit[1];
12905                 }
12906             } else {
12907                 if (psplit[1]) {
12908                     fnum = psplit[0] + dec + psplit[1];
12909                 }
12910             }
12911
12912             return (neg ? '-' : '') + format.replace(/[\d,?\.?]+/, fnum);
12913         },
12914
12915         /**
12916          * Returns a number rendering function that can be reused to apply a number format multiple times efficiently
12917          * @param {String} format Any valid number format string for {@link #number}
12918          * @return {Function} The number formatting function
12919          */
12920         numberRenderer : function(format){
12921             return function(v){
12922                 return Ext.util.Format.number(v, format);
12923             };
12924         },
12925
12926         /**
12927          * Selectively do a plural form of a word based on a numeric value. For example, in a template,
12928          * {commentCount:plural("Comment")}  would result in "1 Comment" if commentCount was 1 or would be "x Comments"
12929          * if the value is 0 or greater than 1.
12930          * @param {Number} value The value to compare against
12931          * @param {String} singular The singular form of the word
12932          * @param {String} plural (optional) The plural form of the word (defaults to the singular with an "s")
12933          */
12934         plural : function(v, s, p){
12935             return v +' ' + (v == 1 ? s : (p ? p : s+'s'));
12936         },
12937
12938         /**
12939          * Converts newline characters to the HTML tag &lt;br/>
12940          * @param {String} The string value to format.
12941          * @return {String} The string with embedded &lt;br/> tags in place of newlines.
12942          */
12943         nl2br : function(v){
12944             return Ext.isEmpty(v) ? '' : v.replace(nl2brRe, '<br/>');
12945         }
12946     }
12947 }();
12948 /**
12949  * @class Ext.XTemplate
12950  * @extends Ext.Template
12951  * <p>A template class that supports advanced functionality like:<div class="mdetail-params"><ul>
12952  * <li>Autofilling arrays using templates and sub-templates</li>
12953  * <li>Conditional processing with basic comparison operators</li>
12954  * <li>Basic math function support</li>
12955  * <li>Execute arbitrary inline code with special built-in template variables</li>
12956  * <li>Custom member functions</li>
12957  * <li>Many special tags and built-in operators that aren't defined as part of
12958  * the API, but are supported in the templates that can be created</li>
12959  * </ul></div></p>
12960  * <p>XTemplate provides the templating mechanism built into:<div class="mdetail-params"><ul>
12961  * <li>{@link Ext.DataView}</li>
12962  * <li>{@link Ext.ListView}</li>
12963  * <li>{@link Ext.form.ComboBox}</li>
12964  * <li>{@link Ext.grid.TemplateColumn}</li>
12965  * <li>{@link Ext.grid.GroupingView}</li>
12966  * <li>{@link Ext.menu.Item}</li>
12967  * <li>{@link Ext.layout.MenuLayout}</li>
12968  * <li>{@link Ext.ColorPalette}</li>
12969  * </ul></div></p>
12970  * 
12971  * <p>For example usage {@link #XTemplate see the constructor}.</p>  
12972  *   
12973  * @constructor
12974  * The {@link Ext.Template#Template Ext.Template constructor} describes
12975  * the acceptable parameters to pass to the constructor. The following
12976  * examples demonstrate all of the supported features.</p>
12977  * 
12978  * <div class="mdetail-params"><ul>
12979  * 
12980  * <li><b><u>Sample Data</u></b> 
12981  * <div class="sub-desc">
12982  * <p>This is the data object used for reference in each code example:</p>
12983  * <pre><code>
12984 var data = {
12985     name: 'Jack Slocum',
12986     title: 'Lead Developer',
12987     company: 'Ext JS, LLC',
12988     email: 'jack@extjs.com',
12989     address: '4 Red Bulls Drive',
12990     city: 'Cleveland',
12991     state: 'Ohio',
12992     zip: '44102',
12993     drinks: ['Red Bull', 'Coffee', 'Water'],
12994     kids: [{
12995         name: 'Sara Grace',
12996         age:3
12997     },{
12998         name: 'Zachary',
12999         age:2
13000     },{
13001         name: 'John James',
13002         age:0
13003     }]
13004 };
13005  * </code></pre>
13006  * </div>
13007  * </li>
13008  * 
13009  * 
13010  * <li><b><u>Auto filling of arrays</u></b> 
13011  * <div class="sub-desc">
13012  * <p>The <b><tt>tpl</tt></b> tag and the <b><tt>for</tt></b> operator are used
13013  * to process the provided data object:
13014  * <ul>
13015  * <li>If the value specified in <tt>for</tt> is an array, it will auto-fill,
13016  * repeating the template block inside the <tt>tpl</tt> tag for each item in the
13017  * array.</li>
13018  * <li>If <tt>for="."</tt> is specified, the data object provided is examined.</li>
13019  * <li>While processing an array, the special variable <tt>{#}</tt>
13020  * will provide the current array index + 1 (starts at 1, not 0).</li>
13021  * </ul>
13022  * </p>
13023  * <pre><code>
13024 &lt;tpl <b>for</b>=".">...&lt;/tpl>       // loop through array at root node
13025 &lt;tpl <b>for</b>="foo">...&lt;/tpl>     // loop through array at foo node
13026 &lt;tpl <b>for</b>="foo.bar">...&lt;/tpl> // loop through array at foo.bar node
13027  * </code></pre>
13028  * Using the sample data above:
13029  * <pre><code>
13030 var tpl = new Ext.XTemplate(
13031     '&lt;p>Kids: ',
13032     '&lt;tpl <b>for</b>=".">',       // process the data.kids node
13033         '&lt;p>{#}. {name}&lt;/p>',  // use current array index to autonumber
13034     '&lt;/tpl>&lt;/p>'
13035 );
13036 tpl.overwrite(panel.body, data.kids); // pass the kids property of the data object
13037  * </code></pre>
13038  * <p>An example illustrating how the <b><tt>for</tt></b> property can be leveraged
13039  * to access specified members of the provided data object to populate the template:</p>
13040  * <pre><code>
13041 var tpl = new Ext.XTemplate(
13042     '&lt;p>Name: {name}&lt;/p>',
13043     '&lt;p>Title: {title}&lt;/p>',
13044     '&lt;p>Company: {company}&lt;/p>',
13045     '&lt;p>Kids: ',
13046     '&lt;tpl <b>for="kids"</b>>',     // interrogate the kids property within the data
13047         '&lt;p>{name}&lt;/p>',
13048     '&lt;/tpl>&lt;/p>'
13049 );
13050 tpl.overwrite(panel.body, data);  // pass the root node of the data object
13051  * </code></pre>
13052  * <p>Flat arrays that contain values (and not objects) can be auto-rendered
13053  * using the special <b><tt>{.}</tt></b> variable inside a loop.  This variable
13054  * will represent the value of the array at the current index:</p>
13055  * <pre><code>
13056 var tpl = new Ext.XTemplate(
13057     '&lt;p>{name}\&#39;s favorite beverages:&lt;/p>',
13058     '&lt;tpl for="drinks">',
13059        '&lt;div> - {.}&lt;/div>',
13060     '&lt;/tpl>'
13061 );
13062 tpl.overwrite(panel.body, data);
13063  * </code></pre>
13064  * <p>When processing a sub-template, for example while looping through a child array,
13065  * you can access the parent object's members via the <b><tt>parent</tt></b> object:</p>
13066  * <pre><code>
13067 var tpl = new Ext.XTemplate(
13068     '&lt;p>Name: {name}&lt;/p>',
13069     '&lt;p>Kids: ',
13070     '&lt;tpl for="kids">',
13071         '&lt;tpl if="age > 1">',
13072             '&lt;p>{name}&lt;/p>',
13073             '&lt;p>Dad: {<b>parent</b>.name}&lt;/p>',
13074         '&lt;/tpl>',
13075     '&lt;/tpl>&lt;/p>'
13076 );
13077 tpl.overwrite(panel.body, data);
13078  * </code></pre>
13079  * </div>
13080  * </li>
13081  * 
13082  * 
13083  * <li><b><u>Conditional processing with basic comparison operators</u></b> 
13084  * <div class="sub-desc">
13085  * <p>The <b><tt>tpl</tt></b> tag and the <b><tt>if</tt></b> operator are used
13086  * to provide conditional checks for deciding whether or not to render specific
13087  * parts of the template. Notes:<div class="sub-desc"><ul>
13088  * <li>Double quotes must be encoded if used within the conditional</li>
13089  * <li>There is no <tt>else</tt> operator &mdash; if needed, two opposite
13090  * <tt>if</tt> statements should be used.</li>
13091  * </ul></div>
13092  * <pre><code>
13093 &lt;tpl if="age &gt; 1 &amp;&amp; age &lt; 10">Child&lt;/tpl>
13094 &lt;tpl if="age >= 10 && age < 18">Teenager&lt;/tpl>
13095 &lt;tpl <b>if</b>="this.isGirl(name)">...&lt;/tpl>
13096 &lt;tpl <b>if</b>="id==\'download\'">...&lt;/tpl>
13097 &lt;tpl <b>if</b>="needsIcon">&lt;img src="{icon}" class="{iconCls}"/>&lt;/tpl>
13098 // no good:
13099 &lt;tpl if="name == "Jack"">Hello&lt;/tpl>
13100 // encode &#34; if it is part of the condition, e.g.
13101 &lt;tpl if="name == &#38;quot;Jack&#38;quot;">Hello&lt;/tpl>
13102  * </code></pre>
13103  * Using the sample data above:
13104  * <pre><code>
13105 var tpl = new Ext.XTemplate(
13106     '&lt;p>Name: {name}&lt;/p>',
13107     '&lt;p>Kids: ',
13108     '&lt;tpl for="kids">',
13109         '&lt;tpl if="age > 1">',
13110             '&lt;p>{name}&lt;/p>',
13111         '&lt;/tpl>',
13112     '&lt;/tpl>&lt;/p>'
13113 );
13114 tpl.overwrite(panel.body, data);
13115  * </code></pre>
13116  * </div>
13117  * </li>
13118  * 
13119  * 
13120  * <li><b><u>Basic math support</u></b> 
13121  * <div class="sub-desc">
13122  * <p>The following basic math operators may be applied directly on numeric
13123  * data values:</p><pre>
13124  * + - * /
13125  * </pre>
13126  * For example:
13127  * <pre><code>
13128 var tpl = new Ext.XTemplate(
13129     '&lt;p>Name: {name}&lt;/p>',
13130     '&lt;p>Kids: ',
13131     '&lt;tpl for="kids">',
13132         '&lt;tpl if="age &amp;gt; 1">',  // <-- Note that the &gt; is encoded
13133             '&lt;p>{#}: {name}&lt;/p>',  // <-- Auto-number each item
13134             '&lt;p>In 5 Years: {age+5}&lt;/p>',  // <-- Basic math
13135             '&lt;p>Dad: {parent.name}&lt;/p>',
13136         '&lt;/tpl>',
13137     '&lt;/tpl>&lt;/p>'
13138 );
13139 tpl.overwrite(panel.body, data);
13140 </code></pre>
13141  * </div>
13142  * </li>
13143  *
13144  * 
13145  * <li><b><u>Execute arbitrary inline code with special built-in template variables</u></b> 
13146  * <div class="sub-desc">
13147  * <p>Anything between <code>{[ ... ]}</code> is considered code to be executed
13148  * in the scope of the template. There are some special variables available in that code:
13149  * <ul>
13150  * <li><b><tt>values</tt></b>: The values in the current scope. If you are using
13151  * scope changing sub-templates, you can change what <tt>values</tt> is.</li>
13152  * <li><b><tt>parent</tt></b>: The scope (values) of the ancestor template.</li>
13153  * <li><b><tt>xindex</tt></b>: If you are in a looping template, the index of the
13154  * loop you are in (1-based).</li>
13155  * <li><b><tt>xcount</tt></b>: If you are in a looping template, the total length
13156  * of the array you are looping.</li>
13157  * <li><b><tt>fm</tt></b>: An alias for <tt>Ext.util.Format</tt>.</li>
13158  * </ul>
13159  * This example demonstrates basic row striping using an inline code block and the
13160  * <tt>xindex</tt> variable:</p>
13161  * <pre><code>
13162 var tpl = new Ext.XTemplate(
13163     '&lt;p>Name: {name}&lt;/p>',
13164     '&lt;p>Company: {[values.company.toUpperCase() + ", " + values.title]}&lt;/p>',
13165     '&lt;p>Kids: ',
13166     '&lt;tpl for="kids">',
13167        '&lt;div class="{[xindex % 2 === 0 ? "even" : "odd"]}">',
13168         '{name}',
13169         '&lt;/div>',
13170     '&lt;/tpl>&lt;/p>'
13171 );
13172 tpl.overwrite(panel.body, data);
13173  * </code></pre>
13174  * </div>
13175  * </li>
13176  * 
13177  * <li><b><u>Template member functions</u></b> 
13178  * <div class="sub-desc">
13179  * <p>One or more member functions can be specified in a configuration
13180  * object passed into the XTemplate constructor for more complex processing:</p>
13181  * <pre><code>
13182 var tpl = new Ext.XTemplate(
13183     '&lt;p>Name: {name}&lt;/p>',
13184     '&lt;p>Kids: ',
13185     '&lt;tpl for="kids">',
13186         '&lt;tpl if="this.isGirl(name)">',
13187             '&lt;p>Girl: {name} - {age}&lt;/p>',
13188         '&lt;/tpl>',
13189         // use opposite if statement to simulate 'else' processing:
13190         '&lt;tpl if="this.isGirl(name) == false">',
13191             '&lt;p>Boy: {name} - {age}&lt;/p>',
13192         '&lt;/tpl>',
13193         '&lt;tpl if="this.isBaby(age)">',
13194             '&lt;p>{name} is a baby!&lt;/p>',
13195         '&lt;/tpl>',
13196     '&lt;/tpl>&lt;/p>',
13197     {
13198         // XTemplate configuration:
13199         compiled: true,
13200         disableFormats: true,
13201         // member functions:
13202         isGirl: function(name){
13203             return name == 'Sara Grace';
13204         },
13205         isBaby: function(age){
13206             return age < 1;
13207         }
13208     }
13209 );
13210 tpl.overwrite(panel.body, data);
13211  * </code></pre>
13212  * </div>
13213  * </li>
13214  * 
13215  * </ul></div>
13216  * 
13217  * @param {Mixed} config
13218  */
13219 Ext.XTemplate = function(){
13220     Ext.XTemplate.superclass.constructor.apply(this, arguments);
13221
13222     var me = this,
13223         s = me.html,
13224         re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
13225         nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
13226         ifRe = /^<tpl\b[^>]*?if="(.*?)"/,
13227         execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
13228         m,
13229         id = 0,
13230         tpls = [],
13231         VALUES = 'values',
13232         PARENT = 'parent',
13233         XINDEX = 'xindex',
13234         XCOUNT = 'xcount',
13235         RETURN = 'return ',
13236         WITHVALUES = 'with(values){ ';
13237
13238     s = ['<tpl>', s, '</tpl>'].join('');
13239
13240     while((m = s.match(re))){
13241         var m2 = m[0].match(nameRe),
13242                         m3 = m[0].match(ifRe),
13243                 m4 = m[0].match(execRe),
13244                 exp = null,
13245                 fn = null,
13246                 exec = null,
13247                 name = m2 && m2[1] ? m2[1] : '';
13248
13249        if (m3) {
13250            exp = m3 && m3[1] ? m3[1] : null;
13251            if(exp){
13252                fn = new Function(VALUES, PARENT, XINDEX, XCOUNT, WITHVALUES + RETURN +(Ext.util.Format.htmlDecode(exp))+'; }');
13253            }
13254        }
13255        if (m4) {
13256            exp = m4 && m4[1] ? m4[1] : null;
13257            if(exp){
13258                exec = new Function(VALUES, PARENT, XINDEX, XCOUNT, WITHVALUES +(Ext.util.Format.htmlDecode(exp))+'; }');
13259            }
13260        }
13261        if(name){
13262            switch(name){
13263                case '.': name = new Function(VALUES, PARENT, WITHVALUES + RETURN + VALUES + '; }'); break;
13264                case '..': name = new Function(VALUES, PARENT, WITHVALUES + RETURN + PARENT + '; }'); break;
13265                default: name = new Function(VALUES, PARENT, WITHVALUES + RETURN + name + '; }');
13266            }
13267        }
13268        tpls.push({
13269             id: id,
13270             target: name,
13271             exec: exec,
13272             test: fn,
13273             body: m[1]||''
13274         });
13275        s = s.replace(m[0], '{xtpl'+ id + '}');
13276        ++id;
13277     }
13278         Ext.each(tpls, function(t) {
13279         me.compileTpl(t);
13280     });
13281     me.master = tpls[tpls.length-1];
13282     me.tpls = tpls;
13283 };
13284 Ext.extend(Ext.XTemplate, Ext.Template, {
13285     // private
13286     re : /\{([\w-\.\#]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?(\s?[\+\-\*\\]\s?[\d\.\+\-\*\\\(\)]+)?\}/g,
13287     // private
13288     codeRe : /\{\[((?:\\\]|.|\n)*?)\]\}/g,
13289
13290     // private
13291     applySubTemplate : function(id, values, parent, xindex, xcount){
13292         var me = this,
13293                 len,
13294                 t = me.tpls[id],
13295                 vs,
13296                 buf = [];
13297         if ((t.test && !t.test.call(me, values, parent, xindex, xcount)) ||
13298             (t.exec && t.exec.call(me, values, parent, xindex, xcount))) {
13299             return '';
13300         }
13301         vs = t.target ? t.target.call(me, values, parent) : values;
13302         len = vs.length;
13303         parent = t.target ? values : parent;
13304         if(t.target && Ext.isArray(vs)){
13305                 Ext.each(vs, function(v, i) {
13306                 buf[buf.length] = t.compiled.call(me, v, parent, i+1, len);
13307             });
13308             return buf.join('');
13309         }
13310         return t.compiled.call(me, vs, parent, xindex, xcount);
13311     },
13312
13313     // private
13314     compileTpl : function(tpl){
13315         var fm = Ext.util.Format,
13316                 useF = this.disableFormats !== true,
13317             sep = Ext.isGecko ? "+" : ",",
13318             body;
13319
13320         function fn(m, name, format, args, math){
13321             if(name.substr(0, 4) == 'xtpl'){
13322                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent, xindex, xcount)'+sep+"'";
13323             }
13324             var v;
13325             if(name === '.'){
13326                 v = 'values';
13327             }else if(name === '#'){
13328                 v = 'xindex';
13329             }else if(name.indexOf('.') != -1){
13330                 v = name;
13331             }else{
13332                 v = "values['" + name + "']";
13333             }
13334             if(math){
13335                 v = '(' + v + math + ')';
13336             }
13337             if (format && useF) {
13338                 args = args ? ',' + args : "";
13339                 if(format.substr(0, 5) != "this."){
13340                     format = "fm." + format + '(';
13341                 }else{
13342                     format = 'this.call("'+ format.substr(5) + '", ';
13343                     args = ", values";
13344                 }
13345             } else {
13346                 args= ''; format = "("+v+" === undefined ? '' : ";
13347             }
13348             return "'"+ sep + format + v + args + ")"+sep+"'";
13349         }
13350
13351         function codeFn(m, code){
13352             // Single quotes get escaped when the template is compiled, however we want to undo this when running code.
13353             return "'" + sep + '(' + code.replace(/\\'/g, "'") + ')' + sep + "'";
13354         }
13355
13356         // branched to use + in gecko and [].join() in others
13357         if(Ext.isGecko){
13358             body = "tpl.compiled = function(values, parent, xindex, xcount){ return '" +
13359                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn).replace(this.codeRe, codeFn) +
13360                     "';};";
13361         }else{
13362             body = ["tpl.compiled = function(values, parent, xindex, xcount){ return ['"];
13363             body.push(tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn).replace(this.codeRe, codeFn));
13364             body.push("'].join('');};");
13365             body = body.join('');
13366         }
13367         eval(body);
13368         return this;
13369     },
13370
13371     /**
13372      * Returns an HTML fragment of this template with the specified values applied.
13373      * @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'})
13374      * @return {String} The HTML fragment
13375      */
13376     applyTemplate : function(values){
13377         return this.master.compiled.call(this, values, {}, 1, 1);
13378     },
13379
13380     /**
13381      * Compile the template to a function for optimized performance.  Recommended if the template will be used frequently.
13382      * @return {Function} The compiled function
13383      */
13384     compile : function(){return this;}
13385
13386     /**
13387      * @property re
13388      * @hide
13389      */
13390     /**
13391      * @property disableFormats
13392      * @hide
13393      */
13394     /**
13395      * @method set
13396      * @hide
13397      */
13398
13399 });
13400 /**
13401  * Alias for {@link #applyTemplate}
13402  * Returns an HTML fragment of this template with the specified values applied.
13403  * @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'})
13404  * @return {String} The HTML fragment
13405  * @member Ext.XTemplate
13406  * @method apply
13407  */
13408 Ext.XTemplate.prototype.apply = Ext.XTemplate.prototype.applyTemplate;
13409
13410 /**
13411  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
13412  * @param {String/HTMLElement} el A DOM element or its id
13413  * @return {Ext.Template} The created template
13414  * @static
13415  */
13416 Ext.XTemplate.from = function(el){
13417     el = Ext.getDom(el);
13418     return new Ext.XTemplate(el.value || el.innerHTML);
13419 };/**
13420  * @class Ext.util.CSS
13421  * Utility class for manipulating CSS rules
13422  * @singleton
13423  */
13424 Ext.util.CSS = function(){
13425         var rules = null;
13426         var doc = document;
13427
13428     var camelRe = /(-[a-z])/gi;
13429     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13430
13431    return {
13432    /**
13433     * Creates a stylesheet from a text blob of rules.
13434     * These rules will be wrapped in a STYLE tag and appended to the HEAD of the document.
13435     * @param {String} cssText The text containing the css rules
13436     * @param {String} id An id to add to the stylesheet for later removal
13437     * @return {StyleSheet}
13438     */
13439    createStyleSheet : function(cssText, id){
13440        var ss;
13441        var head = doc.getElementsByTagName("head")[0];
13442        var rules = doc.createElement("style");
13443        rules.setAttribute("type", "text/css");
13444        if(id){
13445            rules.setAttribute("id", id);
13446        }
13447        if(Ext.isIE){
13448            head.appendChild(rules);
13449            ss = rules.styleSheet;
13450            ss.cssText = cssText;
13451        }else{
13452            try{
13453                 rules.appendChild(doc.createTextNode(cssText));
13454            }catch(e){
13455                rules.cssText = cssText;
13456            }
13457            head.appendChild(rules);
13458            ss = rules.styleSheet ? rules.styleSheet : (rules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13459        }
13460        this.cacheStyleSheet(ss);
13461        return ss;
13462    },
13463
13464    /**
13465     * Removes a style or link tag by id
13466     * @param {String} id The id of the tag
13467     */
13468    removeStyleSheet : function(id){
13469        var existing = doc.getElementById(id);
13470        if(existing){
13471            existing.parentNode.removeChild(existing);
13472        }
13473    },
13474
13475    /**
13476     * Dynamically swaps an existing stylesheet reference for a new one
13477     * @param {String} id The id of an existing link tag to remove
13478     * @param {String} url The href of the new stylesheet to include
13479     */
13480    swapStyleSheet : function(id, url){
13481        this.removeStyleSheet(id);
13482        var ss = doc.createElement("link");
13483        ss.setAttribute("rel", "stylesheet");
13484        ss.setAttribute("type", "text/css");
13485        ss.setAttribute("id", id);
13486        ss.setAttribute("href", url);
13487        doc.getElementsByTagName("head")[0].appendChild(ss);
13488    },
13489    
13490    /**
13491     * Refresh the rule cache if you have dynamically added stylesheets
13492     * @return {Object} An object (hash) of rules indexed by selector
13493     */
13494    refreshCache : function(){
13495        return this.getRules(true);
13496    },
13497
13498    // private
13499    cacheStyleSheet : function(ss){
13500        if(!rules){
13501            rules = {};
13502        }
13503        try{// try catch for cross domain access issue
13504            var ssRules = ss.cssRules || ss.rules;
13505            for(var j = ssRules.length-1; j >= 0; --j){
13506                rules[ssRules[j].selectorText.toLowerCase()] = ssRules[j];
13507            }
13508        }catch(e){}
13509    },
13510    
13511    /**
13512     * Gets all css rules for the document
13513     * @param {Boolean} refreshCache true to refresh the internal cache
13514     * @return {Object} An object (hash) of rules indexed by selector
13515     */
13516    getRules : function(refreshCache){
13517                 if(rules === null || refreshCache){
13518                         rules = {};
13519                         var ds = doc.styleSheets;
13520                         for(var i =0, len = ds.length; i < len; i++){
13521                             try{
13522                         this.cacheStyleSheet(ds[i]);
13523                     }catch(e){} 
13524                 }
13525                 }
13526                 return rules;
13527         },
13528         
13529         /**
13530     * Gets an an individual CSS rule by selector(s)
13531     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
13532     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
13533     * @return {CSSRule} The CSS rule or null if one is not found
13534     */
13535    getRule : function(selector, refreshCache){
13536                 var rs = this.getRules(refreshCache);
13537                 if(!Ext.isArray(selector)){
13538                     return rs[selector.toLowerCase()];
13539                 }
13540                 for(var i = 0; i < selector.length; i++){
13541                         if(rs[selector[i]]){
13542                                 return rs[selector[i].toLowerCase()];
13543                         }
13544                 }
13545                 return null;
13546         },
13547         
13548         
13549         /**
13550     * Updates a rule property
13551     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
13552     * @param {String} property The css property
13553     * @param {String} value The new value for the property
13554     * @return {Boolean} true If a rule was found and updated
13555     */
13556    updateRule : function(selector, property, value){
13557                 if(!Ext.isArray(selector)){
13558                         var rule = this.getRule(selector);
13559                         if(rule){
13560                                 rule.style[property.replace(camelRe, camelFn)] = value;
13561                                 return true;
13562                         }
13563                 }else{
13564                         for(var i = 0; i < selector.length; i++){
13565                                 if(this.updateRule(selector[i], property, value)){
13566                                         return true;
13567                                 }
13568                         }
13569                 }
13570                 return false;
13571         }
13572    };   
13573 }();/**
13574  @class Ext.util.ClickRepeater
13575  @extends Ext.util.Observable
13576
13577  A wrapper class which can be applied to any element. Fires a "click" event while the
13578  mouse is pressed. The interval between firings may be specified in the config but
13579  defaults to 20 milliseconds.
13580
13581  Optionally, a CSS class may be applied to the element during the time it is pressed.
13582
13583  @cfg {Mixed} el The element to act as a button.
13584  @cfg {Number} delay The initial delay before the repeating event begins firing.
13585  Similar to an autorepeat key delay.
13586  @cfg {Number} interval The interval between firings of the "click" event. Default 20 ms.
13587  @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
13588  @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
13589            "interval" and "delay" are ignored.
13590  @cfg {Boolean} preventDefault True to prevent the default click event
13591  @cfg {Boolean} stopDefault True to stop the default click event
13592
13593  @history
13594     2007-02-02 jvs Original code contributed by Nige "Animal" White
13595     2007-02-02 jvs Renamed to ClickRepeater
13596     2007-02-03 jvs Modifications for FF Mac and Safari
13597
13598  @constructor
13599  @param {Mixed} el The element to listen on
13600  @param {Object} config
13601  */
13602 Ext.util.ClickRepeater = function(el, config)
13603 {
13604     this.el = Ext.get(el);
13605     this.el.unselectable();
13606
13607     Ext.apply(this, config);
13608
13609     this.addEvents(
13610     /**
13611      * @event mousedown
13612      * Fires when the mouse button is depressed.
13613      * @param {Ext.util.ClickRepeater} this
13614      */
13615         "mousedown",
13616     /**
13617      * @event click
13618      * Fires on a specified interval during the time the element is pressed.
13619      * @param {Ext.util.ClickRepeater} this
13620      */
13621         "click",
13622     /**
13623      * @event mouseup
13624      * Fires when the mouse key is released.
13625      * @param {Ext.util.ClickRepeater} this
13626      */
13627         "mouseup"
13628     );
13629
13630     if(!this.disabled){
13631         this.disabled = true;
13632         this.enable();
13633     }
13634
13635     // allow inline handler
13636     if(this.handler){
13637         this.on("click", this.handler,  this.scope || this);
13638     }
13639
13640     Ext.util.ClickRepeater.superclass.constructor.call(this);
13641 };
13642
13643 Ext.extend(Ext.util.ClickRepeater, Ext.util.Observable, {
13644     interval : 20,
13645     delay: 250,
13646     preventDefault : true,
13647     stopDefault : false,
13648     timer : 0,
13649
13650     /**
13651      * Enables the repeater and allows events to fire.
13652      */
13653     enable: function(){
13654         if(this.disabled){
13655             this.el.on('mousedown', this.handleMouseDown, this);
13656             if (Ext.isIE){
13657                 this.el.on('dblclick', this.handleDblClick, this);
13658             }
13659             if(this.preventDefault || this.stopDefault){
13660                 this.el.on('click', this.eventOptions, this);
13661             }
13662         }
13663         this.disabled = false;
13664     },
13665
13666     /**
13667      * Disables the repeater and stops events from firing.
13668      */
13669     disable: function(/* private */ force){
13670         if(force || !this.disabled){
13671             clearTimeout(this.timer);
13672             if(this.pressClass){
13673                 this.el.removeClass(this.pressClass);
13674             }
13675             Ext.getDoc().un('mouseup', this.handleMouseUp, this);
13676             this.el.removeAllListeners();
13677         }
13678         this.disabled = true;
13679     },
13680
13681     /**
13682      * Convenience function for setting disabled/enabled by boolean.
13683      * @param {Boolean} disabled
13684      */
13685     setDisabled: function(disabled){
13686         this[disabled ? 'disable' : 'enable']();
13687     },
13688
13689     eventOptions: function(e){
13690         if(this.preventDefault){
13691             e.preventDefault();
13692         }
13693         if(this.stopDefault){
13694             e.stopEvent();
13695         }
13696     },
13697
13698     // private
13699     destroy : function() {
13700         this.disable(true);
13701         Ext.destroy(this.el);
13702         this.purgeListeners();
13703     },
13704
13705     handleDblClick : function(){
13706         clearTimeout(this.timer);
13707         this.el.blur();
13708
13709         this.fireEvent("mousedown", this);
13710         this.fireEvent("click", this);
13711     },
13712
13713     // private
13714     handleMouseDown : function(){
13715         clearTimeout(this.timer);
13716         this.el.blur();
13717         if(this.pressClass){
13718             this.el.addClass(this.pressClass);
13719         }
13720         this.mousedownTime = new Date();
13721
13722         Ext.getDoc().on("mouseup", this.handleMouseUp, this);
13723         this.el.on("mouseout", this.handleMouseOut, this);
13724
13725         this.fireEvent("mousedown", this);
13726         this.fireEvent("click", this);
13727
13728         // Do not honor delay or interval if acceleration wanted.
13729         if (this.accelerate) {
13730             this.delay = 400;
13731         }
13732         this.timer = this.click.defer(this.delay || this.interval, this);
13733     },
13734
13735     // private
13736     click : function(){
13737         this.fireEvent("click", this);
13738         this.timer = this.click.defer(this.accelerate ?
13739             this.easeOutExpo(this.mousedownTime.getElapsed(),
13740                 400,
13741                 -390,
13742                 12000) :
13743             this.interval, this);
13744     },
13745
13746     easeOutExpo : function (t, b, c, d) {
13747         return (t==d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b;
13748     },
13749
13750     // private
13751     handleMouseOut : function(){
13752         clearTimeout(this.timer);
13753         if(this.pressClass){
13754             this.el.removeClass(this.pressClass);
13755         }
13756         this.el.on("mouseover", this.handleMouseReturn, this);
13757     },
13758
13759     // private
13760     handleMouseReturn : function(){
13761         this.el.un("mouseover", this.handleMouseReturn, this);
13762         if(this.pressClass){
13763             this.el.addClass(this.pressClass);
13764         }
13765         this.click();
13766     },
13767
13768     // private
13769     handleMouseUp : function(){
13770         clearTimeout(this.timer);
13771         this.el.un("mouseover", this.handleMouseReturn, this);
13772         this.el.un("mouseout", this.handleMouseOut, this);
13773         Ext.getDoc().un("mouseup", this.handleMouseUp, this);
13774         this.el.removeClass(this.pressClass);
13775         this.fireEvent("mouseup", this);
13776     }
13777 });/**
13778  * @class Ext.KeyNav
13779  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
13780  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
13781  * way to implement custom navigation schemes for any UI component.</p>
13782  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
13783  * pageUp, pageDown, del, home, end.  Usage:</p>
13784  <pre><code>
13785 var nav = new Ext.KeyNav("my-element", {
13786     "left" : function(e){
13787         this.moveLeft(e.ctrlKey);
13788     },
13789     "right" : function(e){
13790         this.moveRight(e.ctrlKey);
13791     },
13792     "enter" : function(e){
13793         this.save();
13794     },
13795     scope : this
13796 });
13797 </code></pre>
13798  * @constructor
13799  * @param {Mixed} el The element to bind to
13800  * @param {Object} config The config
13801  */
13802 Ext.KeyNav = function(el, config){
13803     this.el = Ext.get(el);
13804     Ext.apply(this, config);
13805     if(!this.disabled){
13806         this.disabled = true;
13807         this.enable();
13808     }
13809 };
13810
13811 Ext.KeyNav.prototype = {
13812     /**
13813      * @cfg {Boolean} disabled
13814      * True to disable this KeyNav instance (defaults to false)
13815      */
13816     disabled : false,
13817     /**
13818      * @cfg {String} defaultEventAction
13819      * The method to call on the {@link Ext.EventObject} after this KeyNav intercepts a key.  Valid values are
13820      * {@link Ext.EventObject#stopEvent}, {@link Ext.EventObject#preventDefault} and
13821      * {@link Ext.EventObject#stopPropagation} (defaults to 'stopEvent')
13822      */
13823     defaultEventAction: "stopEvent",
13824     /**
13825      * @cfg {Boolean} forceKeyDown
13826      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
13827      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
13828      * handle keydown instead of keypress.
13829      */
13830     forceKeyDown : false,
13831
13832     // private
13833     relay : function(e){
13834         var k = e.getKey();
13835         var h = this.keyToHandler[k];
13836         if(h && this[h]){
13837             if(this.doRelay(e, this[h], h) !== true){
13838                 e[this.defaultEventAction]();
13839             }
13840         }
13841     },
13842
13843     // private
13844     doRelay : function(e, h, hname){
13845         return h.call(this.scope || this, e);
13846     },
13847
13848     // possible handlers
13849     enter : false,
13850     left : false,
13851     right : false,
13852     up : false,
13853     down : false,
13854     tab : false,
13855     esc : false,
13856     pageUp : false,
13857     pageDown : false,
13858     del : false,
13859     home : false,
13860     end : false,
13861
13862     // quick lookup hash
13863     keyToHandler : {
13864         37 : "left",
13865         39 : "right",
13866         38 : "up",
13867         40 : "down",
13868         33 : "pageUp",
13869         34 : "pageDown",
13870         46 : "del",
13871         36 : "home",
13872         35 : "end",
13873         13 : "enter",
13874         27 : "esc",
13875         9  : "tab"
13876     },
13877     
13878     stopKeyUp: function(e) {
13879         var k = e.getKey();
13880
13881         if (k >= 37 && k <= 40) {
13882             // *** bugfix - safari 2.x fires 2 keyup events on cursor keys
13883             // *** (note: this bugfix sacrifices the "keyup" event originating from keyNav elements in Safari 2)
13884             e.stopEvent();
13885         }
13886     },
13887     
13888     /**
13889      * Destroy this KeyNav (this is the same as calling disable).
13890      */
13891     destroy: function(){
13892         this.disable();    
13893     },
13894
13895         /**
13896          * Enable this KeyNav
13897          */
13898         enable: function() {
13899         if (this.disabled) {
13900             if (Ext.isSafari2) {
13901                 // call stopKeyUp() on "keyup" event
13902                 this.el.on('keyup', this.stopKeyUp, this);
13903             }
13904
13905             this.el.on(this.isKeydown()? 'keydown' : 'keypress', this.relay, this);
13906             this.disabled = false;
13907         }
13908     },
13909
13910         /**
13911          * Disable this KeyNav
13912          */
13913         disable: function() {
13914         if (!this.disabled) {
13915             if (Ext.isSafari2) {
13916                 // remove "keyup" event handler
13917                 this.el.un('keyup', this.stopKeyUp, this);
13918             }
13919
13920             this.el.un(this.isKeydown()? 'keydown' : 'keypress', this.relay, this);
13921             this.disabled = true;
13922         }
13923     },
13924     
13925     /**
13926      * Convenience function for setting disabled/enabled by boolean.
13927      * @param {Boolean} disabled
13928      */
13929     setDisabled : function(disabled){
13930         this[disabled ? "disable" : "enable"]();
13931     },
13932     
13933     // private
13934     isKeydown: function(){
13935         return this.forceKeyDown || Ext.EventManager.useKeydown;
13936     }
13937 };
13938 /**
13939  * @class Ext.KeyMap
13940  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
13941  * The constructor accepts the same config object as defined by {@link #addBinding}.
13942  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
13943  * combination it will call the function with this signature (if the match is a multi-key
13944  * combination the callback will still be called only once): (String key, Ext.EventObject e)
13945  * A KeyMap can also handle a string representation of keys.<br />
13946  * Usage:
13947  <pre><code>
13948 // map one key by key code
13949 var map = new Ext.KeyMap("my-element", {
13950     key: 13, // or Ext.EventObject.ENTER
13951     fn: myHandler,
13952     scope: myObject
13953 });
13954
13955 // map multiple keys to one action by string
13956 var map = new Ext.KeyMap("my-element", {
13957     key: "a\r\n\t",
13958     fn: myHandler,
13959     scope: myObject
13960 });
13961
13962 // map multiple keys to multiple actions by strings and array of codes
13963 var map = new Ext.KeyMap("my-element", [
13964     {
13965         key: [10,13],
13966         fn: function(){ alert("Return was pressed"); }
13967     }, {
13968         key: "abc",
13969         fn: function(){ alert('a, b or c was pressed'); }
13970     }, {
13971         key: "\t",
13972         ctrl:true,
13973         shift:true,
13974         fn: function(){ alert('Control + shift + tab was pressed.'); }
13975     }
13976 ]);
13977 </code></pre>
13978  * <b>Note: A KeyMap starts enabled</b>
13979  * @constructor
13980  * @param {Mixed} el The element to bind to
13981  * @param {Object} config The config (see {@link #addBinding})
13982  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
13983  */
13984 Ext.KeyMap = function(el, config, eventName){
13985     this.el  = Ext.get(el);
13986     this.eventName = eventName || "keydown";
13987     this.bindings = [];
13988     if(config){
13989         this.addBinding(config);
13990     }
13991     this.enable();
13992 };
13993
13994 Ext.KeyMap.prototype = {
13995     /**
13996      * True to stop the event from bubbling and prevent the default browser action if the
13997      * key was handled by the KeyMap (defaults to false)
13998      * @type Boolean
13999      */
14000     stopEvent : false,
14001
14002     /**
14003      * Add a new binding to this KeyMap. The following config object properties are supported:
14004      * <pre>
14005 Property    Type             Description
14006 ----------  ---------------  ----------------------------------------------------------------------
14007 key         String/Array     A single keycode or an array of keycodes to handle
14008 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)
14009 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)
14010 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)
14011 handler     Function         The function to call when KeyMap finds the expected key combination
14012 fn          Function         Alias of handler (for backwards-compatibility)
14013 scope       Object           The scope of the callback function
14014 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)
14015 </pre>
14016      *
14017      * Usage:
14018      * <pre><code>
14019 // Create a KeyMap
14020 var map = new Ext.KeyMap(document, {
14021     key: Ext.EventObject.ENTER,
14022     fn: handleKey,
14023     scope: this
14024 });
14025
14026 //Add a new binding to the existing KeyMap later
14027 map.addBinding({
14028     key: 'abc',
14029     shift: true,
14030     fn: handleKey,
14031     scope: this
14032 });
14033 </code></pre>
14034      * @param {Object/Array} config A single KeyMap config or an array of configs
14035      */
14036         addBinding : function(config){
14037         if(Ext.isArray(config)){
14038             Ext.each(config, function(c){
14039                 this.addBinding(c);
14040             }, this);
14041             return;
14042         }
14043         var keyCode = config.key,
14044             fn = config.fn || config.handler,
14045             scope = config.scope;
14046
14047         if (config.stopEvent) {
14048             this.stopEvent = config.stopEvent;    
14049         }       
14050
14051         if(typeof keyCode == "string"){
14052             var ks = [];
14053             var keyString = keyCode.toUpperCase();
14054             for(var j = 0, len = keyString.length; j < len; j++){
14055                 ks.push(keyString.charCodeAt(j));
14056             }
14057             keyCode = ks;
14058         }
14059         var keyArray = Ext.isArray(keyCode);
14060         
14061         var handler = function(e){
14062             if(this.checkModifiers(config, e)){
14063                 var k = e.getKey();
14064                 if(keyArray){
14065                     for(var i = 0, len = keyCode.length; i < len; i++){
14066                         if(keyCode[i] == k){
14067                           if(this.stopEvent){
14068                               e.stopEvent();
14069                           }
14070                           fn.call(scope || window, k, e);
14071                           return;
14072                         }
14073                     }
14074                 }else{
14075                     if(k == keyCode){
14076                         if(this.stopEvent){
14077                            e.stopEvent();
14078                         }
14079                         fn.call(scope || window, k, e);
14080                     }
14081                 }
14082             }
14083         };
14084         this.bindings.push(handler);
14085         },
14086     
14087     // private
14088     checkModifiers: function(config, e){
14089         var val, key, keys = ['shift', 'ctrl', 'alt'];
14090         for (var i = 0, len = keys.length; i < len; ++i){
14091             key = keys[i];
14092             val = config[key];
14093             if(!(val === undefined || (val === e[key + 'Key']))){
14094                 return false;
14095             }
14096         }
14097         return true;
14098     },
14099
14100     /**
14101      * Shorthand for adding a single key listener
14102      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14103      * following options:
14104      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14105      * @param {Function} fn The function to call
14106      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the browser window.
14107      */
14108     on : function(key, fn, scope){
14109         var keyCode, shift, ctrl, alt;
14110         if(typeof key == "object" && !Ext.isArray(key)){
14111             keyCode = key.key;
14112             shift = key.shift;
14113             ctrl = key.ctrl;
14114             alt = key.alt;
14115         }else{
14116             keyCode = key;
14117         }
14118         this.addBinding({
14119             key: keyCode,
14120             shift: shift,
14121             ctrl: ctrl,
14122             alt: alt,
14123             fn: fn,
14124             scope: scope
14125         });
14126     },
14127
14128     // private
14129     handleKeyDown : function(e){
14130             if(this.enabled){ //just in case
14131             var b = this.bindings;
14132             for(var i = 0, len = b.length; i < len; i++){
14133                 b[i].call(this, e);
14134             }
14135             }
14136         },
14137
14138         /**
14139          * Returns true if this KeyMap is enabled
14140          * @return {Boolean}
14141          */
14142         isEnabled : function(){
14143             return this.enabled;
14144         },
14145
14146         /**
14147          * Enables this KeyMap
14148          */
14149         enable: function(){
14150                 if(!this.enabled){
14151                     this.el.on(this.eventName, this.handleKeyDown, this);
14152                     this.enabled = true;
14153                 }
14154         },
14155
14156         /**
14157          * Disable this KeyMap
14158          */
14159         disable: function(){
14160                 if(this.enabled){
14161                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14162                     this.enabled = false;
14163                 }
14164         },
14165     
14166     /**
14167      * Convenience function for setting disabled/enabled by boolean.
14168      * @param {Boolean} disabled
14169      */
14170     setDisabled : function(disabled){
14171         this[disabled ? "disable" : "enable"]();
14172     }
14173 };/**
14174  * @class Ext.util.TextMetrics
14175  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14176  * wide, in pixels, a given block of text will be. Note that when measuring text, it should be plain text and
14177  * should not contain any HTML, otherwise it may not be measured correctly.
14178  * @singleton
14179  */
14180 Ext.util.TextMetrics = function(){
14181     var shared;
14182     return {
14183         /**
14184          * Measures the size of the specified text
14185          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14186          * that can affect the size of the rendered text
14187          * @param {String} text The text to measure
14188          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14189          * in order to accurately measure the text height
14190          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14191          */
14192         measure : function(el, text, fixedWidth){
14193             if(!shared){
14194                 shared = Ext.util.TextMetrics.Instance(el, fixedWidth);
14195             }
14196             shared.bind(el);
14197             shared.setFixedWidth(fixedWidth || 'auto');
14198             return shared.getSize(text);
14199         },
14200
14201         /**
14202          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14203          * the overhead of multiple calls to initialize the style properties on each measurement.
14204          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14205          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14206          * in order to accurately measure the text height
14207          * @return {Ext.util.TextMetrics.Instance} instance The new instance
14208          */
14209         createInstance : function(el, fixedWidth){
14210             return Ext.util.TextMetrics.Instance(el, fixedWidth);
14211         }
14212     };
14213 }();
14214
14215 Ext.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14216     var ml = new Ext.Element(document.createElement('div'));
14217     document.body.appendChild(ml.dom);
14218     ml.position('absolute');
14219     ml.setLeftTop(-1000, -1000);
14220     ml.hide();
14221
14222     if(fixedWidth){
14223         ml.setWidth(fixedWidth);
14224     }
14225
14226     var instance = {
14227         /**
14228          * <p><b>Only available on the instance returned from {@link #createInstance}, <u>not</u> on the singleton.</b></p>
14229          * Returns the size of the specified text based on the internal element's style and width properties
14230          * @param {String} text The text to measure
14231          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14232          */
14233         getSize : function(text){
14234             ml.update(text);
14235             var s = ml.getSize();
14236             ml.update('');
14237             return s;
14238         },
14239
14240         /**
14241          * <p><b>Only available on the instance returned from {@link #createInstance}, <u>not</u> on the singleton.</b></p>
14242          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14243          * that can affect the size of the rendered text
14244          * @param {String/HTMLElement} el The element, dom node or id
14245          */
14246         bind : function(el){
14247             ml.setStyle(
14248                 Ext.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height', 'text-transform', 'letter-spacing')
14249             );
14250         },
14251
14252         /**
14253          * <p><b>Only available on the instance returned from {@link #createInstance}, <u>not</u> on the singleton.</b></p>
14254          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14255          * to set a fixed width in order to accurately measure the text height.
14256          * @param {Number} width The width to set on the element
14257          */
14258         setFixedWidth : function(width){
14259             ml.setWidth(width);
14260         },
14261
14262         /**
14263          * <p><b>Only available on the instance returned from {@link #createInstance}, <u>not</u> on the singleton.</b></p>
14264          * Returns the measured width of the specified text
14265          * @param {String} text The text to measure
14266          * @return {Number} width The width in pixels
14267          */
14268         getWidth : function(text){
14269             ml.dom.style.width = 'auto';
14270             return this.getSize(text).width;
14271         },
14272
14273         /**
14274          * <p><b>Only available on the instance returned from {@link #createInstance}, <u>not</u> on the singleton.</b></p>
14275          * Returns the measured height of the specified text.  For multiline text, be sure to call
14276          * {@link #setFixedWidth} if necessary.
14277          * @param {String} text The text to measure
14278          * @return {Number} height The height in pixels
14279          */
14280         getHeight : function(text){
14281             return this.getSize(text).height;
14282         }
14283     };
14284
14285     instance.bind(bindTo);
14286
14287     return instance;
14288 };
14289
14290 Ext.Element.addMethods({
14291     /**
14292      * Returns the width in pixels of the passed text, or the width of the text in this Element.
14293      * @param {String} text The text to measure. Defaults to the innerHTML of the element.
14294      * @param {Number} min (Optional) The minumum value to return.
14295      * @param {Number} max (Optional) The maximum value to return.
14296      * @return {Number} The text width in pixels.
14297      * @member Ext.Element getTextWidth
14298      */
14299     getTextWidth : function(text, min, max){
14300         return (Ext.util.TextMetrics.measure(this.dom, Ext.value(text, this.dom.innerHTML, true)).width).constrain(min || 0, max || 1000000);
14301     }
14302 });
14303 /**
14304  * @class Ext.util.Cookies
14305  * Utility class for managing and interacting with cookies.
14306  * @singleton
14307  */
14308 Ext.util.Cookies = {
14309     /**
14310      * Create a cookie with the specified name and value. Additional settings
14311      * for the cookie may be optionally specified (for example: expiration,
14312      * access restriction, SSL).
14313      * @param {String} name The name of the cookie to set. 
14314      * @param {Mixed} value The value to set for the cookie.
14315      * @param {Object} expires (Optional) Specify an expiration date the
14316      * cookie is to persist until.  Note that the specified Date object will
14317      * be converted to Greenwich Mean Time (GMT). 
14318      * @param {String} path (Optional) Setting a path on the cookie restricts
14319      * access to pages that match that path. Defaults to all pages (<tt>'/'</tt>). 
14320      * @param {String} domain (Optional) Setting a domain restricts access to
14321      * pages on a given domain (typically used to allow cookie access across
14322      * subdomains). For example, "extjs.com" will create a cookie that can be
14323      * accessed from any subdomain of extjs.com, including www.extjs.com,
14324      * support.extjs.com, etc.
14325      * @param {Boolean} secure (Optional) Specify true to indicate that the cookie
14326      * should only be accessible via SSL on a page using the HTTPS protocol.
14327      * Defaults to <tt>false</tt>. Note that this will only work if the page
14328      * calling this code uses the HTTPS protocol, otherwise the cookie will be
14329      * created with default options.
14330      */
14331     set : function(name, value){
14332         var argv = arguments;
14333         var argc = arguments.length;
14334         var expires = (argc > 2) ? argv[2] : null;
14335         var path = (argc > 3) ? argv[3] : '/';
14336         var domain = (argc > 4) ? argv[4] : null;
14337         var secure = (argc > 5) ? argv[5] : false;
14338         document.cookie = name + "=" + escape(value) + ((expires === null) ? "" : ("; expires=" + expires.toGMTString())) + ((path === null) ? "" : ("; path=" + path)) + ((domain === null) ? "" : ("; domain=" + domain)) + ((secure === true) ? "; secure" : "");
14339     },
14340
14341     /**
14342      * Retrieves cookies that are accessible by the current page. If a cookie
14343      * does not exist, <code>get()</code> returns <tt>null</tt>.  The following
14344      * example retrieves the cookie called "valid" and stores the String value
14345      * in the variable <tt>validStatus</tt>.
14346      * <pre><code>
14347      * var validStatus = Ext.util.Cookies.get("valid");
14348      * </code></pre>
14349      * @param {String} name The name of the cookie to get
14350      * @return {Mixed} Returns the cookie value for the specified name;
14351      * null if the cookie name does not exist.
14352      */
14353     get : function(name){
14354         var arg = name + "=";
14355         var alen = arg.length;
14356         var clen = document.cookie.length;
14357         var i = 0;
14358         var j = 0;
14359         while(i < clen){
14360             j = i + alen;
14361             if(document.cookie.substring(i, j) == arg){
14362                 return Ext.util.Cookies.getCookieVal(j);
14363             }
14364             i = document.cookie.indexOf(" ", i) + 1;
14365             if(i === 0){
14366                 break;
14367             }
14368         }
14369         return null;
14370     },
14371
14372     /**
14373      * Removes a cookie with the provided name from the browser
14374      * if found by setting its expiration date to sometime in the past. 
14375      * @param {String} name The name of the cookie to remove
14376      */
14377     clear : function(name){
14378         if(Ext.util.Cookies.get(name)){
14379             document.cookie = name + "=" + "; expires=Thu, 01-Jan-70 00:00:01 GMT";
14380         }
14381     },
14382     /**
14383      * @private
14384      */
14385     getCookieVal : function(offset){
14386         var endstr = document.cookie.indexOf(";", offset);
14387         if(endstr == -1){
14388             endstr = document.cookie.length;
14389         }
14390         return unescape(document.cookie.substring(offset, endstr));
14391     }
14392 };/**
14393  * Framework-wide error-handler.  Developers can override this method to provide
14394  * custom exception-handling.  Framework errors will often extend from the base
14395  * Ext.Error class.
14396  * @param {Object/Error} e The thrown exception object.
14397  */
14398 Ext.handleError = function(e) {
14399     throw e;
14400 };
14401
14402 /**
14403  * @class Ext.Error
14404  * @extends Error
14405  * <p>A base error class. Future implementations are intended to provide more
14406  * robust error handling throughout the framework (<b>in the debug build only</b>)
14407  * to check for common errors and problems. The messages issued by this class
14408  * will aid error checking. Error checks will be automatically removed in the
14409  * production build so that performance is not negatively impacted.</p>
14410  * <p>Some sample messages currently implemented:</p><pre>
14411 "DataProxy attempted to execute an API-action but found an undefined
14412 url / function. Please review your Proxy url/api-configuration."
14413  * </pre><pre>
14414 "Could not locate your "root" property in your server response.
14415 Please review your JsonReader config to ensure the config-property
14416 "root" matches the property your server-response.  See the JsonReader
14417 docs for additional assistance."
14418  * </pre>
14419  * <p>An example of the code used for generating error messages:</p><pre><code>
14420 try {
14421     generateError({
14422         foo: 'bar'
14423     });
14424 }
14425 catch (e) {
14426     console.error(e);
14427 }
14428 function generateError(data) {
14429     throw new Ext.Error('foo-error', data);
14430 }
14431  * </code></pre>
14432  * @param {String} message
14433  */
14434 Ext.Error = function(message) {
14435     // Try to read the message from Ext.Error.lang
14436     this.message = (this.lang[message]) ? this.lang[message] : message;
14437 };
14438
14439 Ext.Error.prototype = new Error();
14440 Ext.apply(Ext.Error.prototype, {
14441     // protected.  Extensions place their error-strings here.
14442     lang: {},
14443
14444     name: 'Ext.Error',
14445     /**
14446      * getName
14447      * @return {String}
14448      */
14449     getName : function() {
14450         return this.name;
14451     },
14452     /**
14453      * getMessage
14454      * @return {String}
14455      */
14456     getMessage : function() {
14457         return this.message;
14458     },
14459     /**
14460      * toJson
14461      * @return {String}
14462      */
14463     toJson : function() {
14464         return Ext.encode(this);
14465     }
14466 });