Upgrade to ExtJS 3.2.2 - Released 06/02/2010
[extjs.git] / pkgs / ext-foundation-debug.js
1 /*!
2  * Ext JS Library 3.2.2
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         confRe = /tag|children|cn|html$/i,
140         tableElRe = /td|tr|tbody/i,
141         cssRe = /([a-z0-9-]+)\s*:\s*([^;\s]+(?:\s*[^;\s]+)*);?/gi,
142         endRe = /end/i,
143         pub,
144         // kill repeat to save bytes
145         afterbegin = 'afterbegin',
146         afterend = 'afterend',
147         beforebegin = 'beforebegin',
148         beforeend = 'beforeend',
149         ts = '<table>',
150         te = '</table>',
151         tbs = ts+'<tbody>',
152         tbe = '</tbody>'+te,
153         trs = tbs + '<tr>',
154         tre = '</tr>'+tbe;
155
156     // private
157     function doInsert(el, o, returnElement, pos, sibling, append){
158         var newNode = pub.insertHtml(pos, Ext.getDom(el), createHtml(o));
159         return returnElement ? Ext.get(newNode, true) : newNode;
160     }
161
162     // build as innerHTML where available
163     function createHtml(o){
164         var b = '',
165             attr,
166             val,
167             key,
168             cn;
169
170         if(typeof o == "string"){
171             b = o;
172         } else if (Ext.isArray(o)) {
173             for (var i=0; i < o.length; i++) {
174                 if(o[i]) {
175                     b += createHtml(o[i]);
176                 }
177             };
178         } else {
179             b += '<' + (o.tag = o.tag || 'div');
180             for (attr in o) {
181                 val = o[attr];
182                 if(!confRe.test(attr)){
183                     if (typeof val == "object") {
184                         b += ' ' + attr + '="';
185                         for (key in val) {
186                             b += key + ':' + val[key] + ';';
187                         };
188                         b += '"';
189                     }else{
190                         b += ' ' + ({cls : 'class', htmlFor : 'for'}[attr] || attr) + '="' + val + '"';
191                     }
192                 }
193             };
194             // Now either just close the tag or try to add children and close the tag.
195             if (emptyTags.test(o.tag)) {
196                 b += '/>';
197             } else {
198                 b += '>';
199                 if ((cn = o.children || o.cn)) {
200                     b += createHtml(cn);
201                 } else if(o.html){
202                     b += o.html;
203                 }
204                 b += '</' + o.tag + '>';
205             }
206         }
207         return b;
208     }
209
210     function ieTable(depth, s, h, e){
211         tempTableEl.innerHTML = [s, h, e].join('');
212         var i = -1,
213             el = tempTableEl,
214             ns;
215         while(++i < depth){
216             el = el.firstChild;
217         }
218 //      If the result is multiple siblings, then encapsulate them into one fragment.
219         if(ns = el.nextSibling){
220             var df = document.createDocumentFragment();
221             while(el){
222                 ns = el.nextSibling;
223                 df.appendChild(el);
224                 el = ns;
225             }
226             el = df;
227         }
228         return el;
229     }
230
231     /**
232      * @ignore
233      * Nasty code for IE's broken table implementation
234      */
235     function insertIntoTable(tag, where, el, html) {
236         var node,
237             before;
238
239         tempTableEl = tempTableEl || document.createElement('div');
240
241         if(tag == 'td' && (where == afterbegin || where == beforeend) ||
242            !tableElRe.test(tag) && (where == beforebegin || where == afterend)) {
243             return;
244         }
245         before = where == beforebegin ? el :
246                  where == afterend ? el.nextSibling :
247                  where == afterbegin ? el.firstChild : null;
248
249         if (where == beforebegin || where == afterend) {
250             el = el.parentNode;
251         }
252
253         if (tag == 'td' || (tag == 'tr' && (where == beforeend || where == afterbegin))) {
254             node = ieTable(4, trs, html, tre);
255         } else if ((tag == 'tbody' && (where == beforeend || where == afterbegin)) ||
256                    (tag == 'tr' && (where == beforebegin || where == afterend))) {
257             node = ieTable(3, tbs, html, tbe);
258         } else {
259             node = ieTable(2, ts, html, te);
260         }
261         el.insertBefore(node, before);
262         return node;
263     }
264
265
266     pub = {
267         /**
268          * Returns the markup for the passed Element(s) config.
269          * @param {Object} o The DOM object spec (and children)
270          * @return {String}
271          */
272         markup : function(o){
273             return createHtml(o);
274         },
275
276         /**
277          * Applies a style specification to an element.
278          * @param {String/HTMLElement} el The element to apply styles to
279          * @param {String/Object/Function} styles A style specification string e.g. 'width:100px', or object in the form {width:'100px'}, or
280          * a function which returns such a specification.
281          */
282         applyStyles : function(el, styles){
283             if (styles) {
284                 var matches;
285
286                 el = Ext.fly(el);
287                 if (typeof styles == "function") {
288                     styles = styles.call();
289                 }
290                 if (typeof styles == "string") {
291                     while ((matches = cssRe.exec(styles))) {
292                         el.setStyle(matches[1], matches[2]);
293                     }
294                 } else if (typeof styles == "object") {
295                     el.setStyle(styles);
296                 }
297             }
298         },
299
300         /**
301          * Inserts an HTML fragment into the DOM.
302          * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
303          * @param {HTMLElement} el The context element
304          * @param {String} html The HTML fragment
305          * @return {HTMLElement} The new node
306          */
307         insertHtml : function(where, el, html){
308             var hash = {},
309                 hashVal,
310                 setStart,
311                 range,
312                 frag,
313                 rangeEl,
314                 rs;
315
316             where = where.toLowerCase();
317             // add these here because they are used in both branches of the condition.
318             hash[beforebegin] = ['BeforeBegin', 'previousSibling'];
319             hash[afterend] = ['AfterEnd', 'nextSibling'];
320
321             if (el.insertAdjacentHTML) {
322                 if(tableRe.test(el.tagName) && (rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html))){
323                     return rs;
324                 }
325                 // add these two to the hash.
326                 hash[afterbegin] = ['AfterBegin', 'firstChild'];
327                 hash[beforeend] = ['BeforeEnd', 'lastChild'];
328                 if ((hashVal = hash[where])) {
329                     el.insertAdjacentHTML(hashVal[0], html);
330                     return el[hashVal[1]];
331                 }
332             } else {
333                 range = el.ownerDocument.createRange();
334                 setStart = 'setStart' + (endRe.test(where) ? 'After' : 'Before');
335                 if (hash[where]) {
336                     range[setStart](el);
337                     frag = range.createContextualFragment(html);
338                     el.parentNode.insertBefore(frag, where == beforebegin ? el : el.nextSibling);
339                     return el[(where == beforebegin ? 'previous' : 'next') + 'Sibling'];
340                 } else {
341                     rangeEl = (where == afterbegin ? 'first' : 'last') + 'Child';
342                     if (el.firstChild) {
343                         range[setStart](el[rangeEl]);
344                         frag = range.createContextualFragment(html);
345                         if(where == afterbegin){
346                             el.insertBefore(frag, el.firstChild);
347                         }else{
348                             el.appendChild(frag);
349                         }
350                     } else {
351                         el.innerHTML = html;
352                     }
353                     return el[rangeEl];
354                 }
355             }
356             throw 'Illegal insertion point -> "' + where + '"';
357         },
358
359         /**
360          * Creates new DOM element(s) and inserts them before el.
361          * @param {Mixed} el The context element
362          * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
363          * @param {Boolean} returnElement (optional) true to return a Ext.Element
364          * @return {HTMLElement/Ext.Element} The new node
365          */
366         insertBefore : function(el, o, returnElement){
367             return doInsert(el, o, returnElement, beforebegin);
368         },
369
370         /**
371          * Creates new DOM element(s) and inserts them after el.
372          * @param {Mixed} el The context element
373          * @param {Object} o The DOM object spec (and children)
374          * @param {Boolean} returnElement (optional) true to return a Ext.Element
375          * @return {HTMLElement/Ext.Element} The new node
376          */
377         insertAfter : function(el, o, returnElement){
378             return doInsert(el, o, returnElement, afterend, 'nextSibling');
379         },
380
381         /**
382          * Creates new DOM element(s) and inserts them as the first child of el.
383          * @param {Mixed} el The context element
384          * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
385          * @param {Boolean} returnElement (optional) true to return a Ext.Element
386          * @return {HTMLElement/Ext.Element} The new node
387          */
388         insertFirst : function(el, o, returnElement){
389             return doInsert(el, o, returnElement, afterbegin, 'firstChild');
390         },
391
392         /**
393          * Creates new DOM element(s) and appends them to el.
394          * @param {Mixed} el The context element
395          * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
396          * @param {Boolean} returnElement (optional) true to return a Ext.Element
397          * @return {HTMLElement/Ext.Element} The new node
398          */
399         append : function(el, o, returnElement){
400             return doInsert(el, o, returnElement, beforeend, '', true);
401         },
402
403         /**
404          * Creates new DOM element(s) and overwrites the contents of el with them.
405          * @param {Mixed} el The context element
406          * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
407          * @param {Boolean} returnElement (optional) true to return a Ext.Element
408          * @return {HTMLElement/Ext.Element} The new node
409          */
410         overwrite : function(el, o, returnElement){
411             el = Ext.getDom(el);
412             el.innerHTML = createHtml(o);
413             return returnElement ? Ext.get(el.firstChild) : el.firstChild;
414         },
415
416         createHtml : createHtml
417     };
418     return pub;
419 }();
420 /**
421  * @class Ext.DomHelper
422  */
423 Ext.apply(Ext.DomHelper,
424 function(){
425     var pub,
426         afterbegin = 'afterbegin',
427         afterend = 'afterend',
428         beforebegin = 'beforebegin',
429         beforeend = 'beforeend',
430         confRe = /tag|children|cn|html$/i;
431
432     // private
433     function doInsert(el, o, returnElement, pos, sibling, append){
434         el = Ext.getDom(el);
435         var newNode;
436         if (pub.useDom) {
437             newNode = createDom(o, null);
438             if (append) {
439                 el.appendChild(newNode);
440             } else {
441                 (sibling == 'firstChild' ? el : el.parentNode).insertBefore(newNode, el[sibling] || el);
442             }
443         } else {
444             newNode = Ext.DomHelper.insertHtml(pos, el, Ext.DomHelper.createHtml(o));
445         }
446         return returnElement ? Ext.get(newNode, true) : newNode;
447     }
448
449     // build as dom
450     /** @ignore */
451     function createDom(o, parentNode){
452         var el,
453             doc = document,
454             useSet,
455             attr,
456             val,
457             cn;
458
459         if (Ext.isArray(o)) {                       // Allow Arrays of siblings to be inserted
460             el = doc.createDocumentFragment(); // in one shot using a DocumentFragment
461             for (var i = 0, l = o.length; i < l; i++) {
462                 createDom(o[i], el);
463             }
464         } else if (typeof o == 'string') {         // Allow a string as a child spec.
465             el = doc.createTextNode(o);
466         } else {
467             el = doc.createElement( o.tag || 'div' );
468             useSet = !!el.setAttribute; // In IE some elements don't have setAttribute
469             for (var attr in o) {
470                 if(!confRe.test(attr)){
471                     val = o[attr];
472                     if(attr == 'cls'){
473                         el.className = val;
474                     }else{
475                         if(useSet){
476                             el.setAttribute(attr, val);
477                         }else{
478                             el[attr] = val;
479                         }
480                     }
481                 }
482             }
483             Ext.DomHelper.applyStyles(el, o.style);
484
485             if ((cn = o.children || o.cn)) {
486                 createDom(cn, el);
487             } else if (o.html) {
488                 el.innerHTML = o.html;
489             }
490         }
491         if(parentNode){
492            parentNode.appendChild(el);
493         }
494         return el;
495     }
496
497     pub = {
498         /**
499          * Creates a new Ext.Template from the DOM object spec.
500          * @param {Object} o The DOM object spec (and children)
501          * @return {Ext.Template} The new template
502          */
503         createTemplate : function(o){
504             var html = Ext.DomHelper.createHtml(o);
505             return new Ext.Template(html);
506         },
507
508         /** True to force the use of DOM instead of html fragments @type Boolean */
509         useDom : false,
510
511         /**
512          * Creates new DOM element(s) and inserts them before el.
513          * @param {Mixed} el The context element
514          * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
515          * @param {Boolean} returnElement (optional) true to return a Ext.Element
516          * @return {HTMLElement/Ext.Element} The new node
517          * @hide (repeat)
518          */
519         insertBefore : function(el, o, returnElement){
520             return doInsert(el, o, returnElement, beforebegin);
521         },
522
523         /**
524          * Creates new DOM element(s) and inserts them after el.
525          * @param {Mixed} el The context element
526          * @param {Object} o The DOM object spec (and children)
527          * @param {Boolean} returnElement (optional) true to return a Ext.Element
528          * @return {HTMLElement/Ext.Element} The new node
529          * @hide (repeat)
530          */
531         insertAfter : function(el, o, returnElement){
532             return doInsert(el, o, returnElement, afterend, 'nextSibling');
533         },
534
535         /**
536          * Creates new DOM element(s) and inserts them as the first child of el.
537          * @param {Mixed} el The context element
538          * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
539          * @param {Boolean} returnElement (optional) true to return a Ext.Element
540          * @return {HTMLElement/Ext.Element} The new node
541          * @hide (repeat)
542          */
543         insertFirst : function(el, o, returnElement){
544             return doInsert(el, o, returnElement, afterbegin, 'firstChild');
545         },
546
547         /**
548          * Creates new DOM element(s) and appends them to el.
549          * @param {Mixed} el The context element
550          * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
551          * @param {Boolean} returnElement (optional) true to return a Ext.Element
552          * @return {HTMLElement/Ext.Element} The new node
553          * @hide (repeat)
554          */
555         append: function(el, o, returnElement){
556             return doInsert(el, o, returnElement, beforeend, '', true);
557         },
558
559         /**
560          * Creates new DOM element(s) without inserting them to the document.
561          * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
562          * @return {HTMLElement} The new uninserted node
563          */
564         createDom: createDom
565     };
566     return pub;
567 }());
568 /**
569  * @class Ext.Template
570  * <p>Represents an HTML fragment template. Templates may be {@link #compile precompiled}
571  * for greater performance.</p>
572  * <p>For example usage {@link #Template see the constructor}.</p>
573  *
574  * @constructor
575  * An instance of this class may be created by passing to the constructor either
576  * a single argument, or multiple arguments:
577  * <div class="mdetail-params"><ul>
578  * <li><b>single argument</b> : String/Array
579  * <div class="sub-desc">
580  * The single argument may be either a String or an Array:<ul>
581  * <li><tt>String</tt> : </li><pre><code>
582 var t = new Ext.Template("&lt;div>Hello {0}.&lt;/div>");
583 t.{@link #append}('some-element', ['foo']);
584  * </code></pre>
585  * <li><tt>Array</tt> : </li>
586  * An Array will be combined with <code>join('')</code>.
587 <pre><code>
588 var t = new Ext.Template([
589     '&lt;div name="{id}"&gt;',
590         '&lt;span class="{cls}"&gt;{name:trim} {value:ellipsis(10)}&lt;/span&gt;',
591     '&lt;/div&gt;',
592 ]);
593 t.{@link #compile}();
594 t.{@link #append}('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
595 </code></pre>
596  * </ul></div></li>
597  * <li><b>multiple arguments</b> : String, Object, Array, ...
598  * <div class="sub-desc">
599  * Multiple arguments will be combined with <code>join('')</code>.
600  * <pre><code>
601 var t = new Ext.Template(
602     '&lt;div name="{id}"&gt;',
603         '&lt;span class="{cls}"&gt;{name} {value}&lt;/span&gt;',
604     '&lt;/div&gt;',
605     // a configuration object:
606     {
607         compiled: true,      // {@link #compile} immediately
608         disableFormats: true // See Notes below.
609     }
610 );
611  * </code></pre>
612  * <p><b>Notes</b>:</p>
613  * <div class="mdetail-params"><ul>
614  * <li>Formatting and <code>disableFormats</code> are not applicable for Ext Core.</li>
615  * <li>For a list of available format functions, see {@link Ext.util.Format}.</li>
616  * <li><code>disableFormats</code> reduces <code>{@link #apply}</code> time
617  * when no formatting is required.</li>
618  * </ul></div>
619  * </div></li>
620  * </ul></div>
621  * @param {Mixed} config
622  */
623 Ext.Template = function(html){
624     var me = this,
625         a = arguments,
626         buf = [],
627         v;
628
629     if (Ext.isArray(html)) {
630         html = html.join("");
631     } else if (a.length > 1) {
632         for(var i = 0, len = a.length; i < len; i++){
633             v = a[i];
634             if(typeof v == 'object'){
635                 Ext.apply(me, v);
636             } else {
637                 buf.push(v);
638             }
639         };
640         html = buf.join('');
641     }
642
643     /**@private*/
644     me.html = html;
645     /**
646      * @cfg {Boolean} compiled Specify <tt>true</tt> to compile the template
647      * immediately (see <code>{@link #compile}</code>).
648      * Defaults to <tt>false</tt>.
649      */
650     if (me.compiled) {
651         me.compile();
652     }
653 };
654 Ext.Template.prototype = {
655     /**
656      * @cfg {RegExp} re The regular expression used to match template variables.
657      * Defaults to:<pre><code>
658      * re : /\{([\w-]+)\}/g                                     // for Ext Core
659      * re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g      // for Ext JS
660      * </code></pre>
661      */
662     re : /\{([\w-]+)\}/g,
663     /**
664      * See <code>{@link #re}</code>.
665      * @type RegExp
666      * @property re
667      */
668
669     /**
670      * Returns an HTML fragment of this template with the specified <code>values</code> applied.
671      * @param {Object/Array} values
672      * The template values. Can be an array if the params are numeric (i.e. <code>{0}</code>)
673      * or an object (i.e. <code>{foo: 'bar'}</code>).
674      * @return {String} The HTML fragment
675      */
676     applyTemplate : function(values){
677         var me = this;
678
679         return me.compiled ?
680                 me.compiled(values) :
681                 me.html.replace(me.re, function(m, name){
682                     return values[name] !== undefined ? values[name] : "";
683                 });
684     },
685
686     /**
687      * Sets the HTML used as the template and optionally compiles it.
688      * @param {String} html
689      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
690      * @return {Ext.Template} this
691      */
692     set : function(html, compile){
693         var me = this;
694         me.html = html;
695         me.compiled = null;
696         return compile ? me.compile() : me;
697     },
698
699     /**
700      * Compiles the template into an internal function, eliminating the RegEx overhead.
701      * @return {Ext.Template} this
702      */
703     compile : function(){
704         var me = this,
705             sep = Ext.isGecko ? "+" : ",";
706
707         function fn(m, name){
708             name = "values['" + name + "']";
709             return "'"+ sep + '(' + name + " == undefined ? '' : " + name + ')' + sep + "'";
710         }
711
712         eval("this.compiled = function(values){ return " + (Ext.isGecko ? "'" : "['") +
713              me.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
714              (Ext.isGecko ?  "';};" : "'].join('');};"));
715         return me;
716     },
717
718     /**
719      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
720      * @param {Mixed} el The context element
721      * @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'})
722      * @param {Boolean} returnElement (optional) true to return a Ext.Element (defaults to undefined)
723      * @return {HTMLElement/Ext.Element} The new node or Element
724      */
725     insertFirst: function(el, values, returnElement){
726         return this.doInsert('afterBegin', el, values, returnElement);
727     },
728
729     /**
730      * Applies the supplied values to the template and inserts the new node(s) before el.
731      * @param {Mixed} el The context element
732      * @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'})
733      * @param {Boolean} returnElement (optional) true to return a Ext.Element (defaults to undefined)
734      * @return {HTMLElement/Ext.Element} The new node or Element
735      */
736     insertBefore: function(el, values, returnElement){
737         return this.doInsert('beforeBegin', el, values, returnElement);
738     },
739
740     /**
741      * Applies the supplied values to the template and inserts the new node(s) after el.
742      * @param {Mixed} el The context element
743      * @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'})
744      * @param {Boolean} returnElement (optional) true to return a Ext.Element (defaults to undefined)
745      * @return {HTMLElement/Ext.Element} The new node or Element
746      */
747     insertAfter : function(el, values, returnElement){
748         return this.doInsert('afterEnd', el, values, returnElement);
749     },
750
751     /**
752      * Applies the supplied <code>values</code> to the template and appends
753      * the new node(s) to the specified <code>el</code>.
754      * <p>For example usage {@link #Template see the constructor}.</p>
755      * @param {Mixed} el The context element
756      * @param {Object/Array} values
757      * The template values. Can be an array if the params are numeric (i.e. <code>{0}</code>)
758      * or an object (i.e. <code>{foo: 'bar'}</code>).
759      * @param {Boolean} returnElement (optional) true to return an Ext.Element (defaults to undefined)
760      * @return {HTMLElement/Ext.Element} The new node or Element
761      */
762     append : function(el, values, returnElement){
763         return this.doInsert('beforeEnd', el, values, returnElement);
764     },
765
766     doInsert : function(where, el, values, returnEl){
767         el = Ext.getDom(el);
768         var newNode = Ext.DomHelper.insertHtml(where, el, this.applyTemplate(values));
769         return returnEl ? Ext.get(newNode, true) : newNode;
770     },
771
772     /**
773      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
774      * @param {Mixed} el The context element
775      * @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'})
776      * @param {Boolean} returnElement (optional) true to return a Ext.Element (defaults to undefined)
777      * @return {HTMLElement/Ext.Element} The new node or Element
778      */
779     overwrite : function(el, values, returnElement){
780         el = Ext.getDom(el);
781         el.innerHTML = this.applyTemplate(values);
782         return returnElement ? Ext.get(el.firstChild, true) : el.firstChild;
783     }
784 };
785 /**
786  * Alias for {@link #applyTemplate}
787  * Returns an HTML fragment of this template with the specified <code>values</code> applied.
788  * @param {Object/Array} values
789  * The template values. Can be an array if the params are numeric (i.e. <code>{0}</code>)
790  * or an object (i.e. <code>{foo: 'bar'}</code>).
791  * @return {String} The HTML fragment
792  * @member Ext.Template
793  * @method apply
794  */
795 Ext.Template.prototype.apply = Ext.Template.prototype.applyTemplate;
796
797 /**
798  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
799  * @param {String/HTMLElement} el A DOM element or its id
800  * @param {Object} config A configuration object
801  * @return {Ext.Template} The created template
802  * @static
803  */
804 Ext.Template.from = function(el, config){
805     el = Ext.getDom(el);
806     return new Ext.Template(el.value || el.innerHTML, config || '');
807 };
808 /**
809  * @class Ext.Template
810  */
811 Ext.apply(Ext.Template.prototype, {
812     /**
813      * @cfg {Boolean} disableFormats Specify <tt>true</tt> to disable format
814      * functions in the template. If the template does not contain
815      * {@link Ext.util.Format format functions}, setting <code>disableFormats</code>
816      * to true will reduce <code>{@link #apply}</code> time. Defaults to <tt>false</tt>.
817      * <pre><code>
818 var t = new Ext.Template(
819     '&lt;div name="{id}"&gt;',
820         '&lt;span class="{cls}"&gt;{name} {value}&lt;/span&gt;',
821     '&lt;/div&gt;',
822     {
823         compiled: true,      // {@link #compile} immediately
824         disableFormats: true // reduce <code>{@link #apply}</code> time since no formatting
825     }
826 );
827      * </code></pre>
828      * For a list of available format functions, see {@link Ext.util.Format}.
829      */
830     disableFormats : false,
831     /**
832      * See <code>{@link #disableFormats}</code>.
833      * @type Boolean
834      * @property disableFormats
835      */
836
837     /**
838      * The regular expression used to match template variables
839      * @type RegExp
840      * @property
841      * @hide repeat doc
842      */
843     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
844     argsRe : /^\s*['"](.*)["']\s*$/,
845     compileARe : /\\/g,
846     compileBRe : /(\r\n|\n)/g,
847     compileCRe : /'/g,
848
849     /**
850      * Returns an HTML fragment of this template with the specified values applied.
851      * @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'})
852      * @return {String} The HTML fragment
853      * @hide repeat doc
854      */
855     applyTemplate : function(values){
856         var me = this,
857             useF = me.disableFormats !== true,
858             fm = Ext.util.Format,
859             tpl = me;
860
861         if(me.compiled){
862             return me.compiled(values);
863         }
864         function fn(m, name, format, args){
865             if (format && useF) {
866                 if (format.substr(0, 5) == "this.") {
867                     return tpl.call(format.substr(5), values[name], values);
868                 } else {
869                     if (args) {
870                         // quoted values are required for strings in compiled templates,
871                         // but for non compiled we need to strip them
872                         // quoted reversed for jsmin
873                         var re = me.argsRe;
874                         args = args.split(',');
875                         for(var i = 0, len = args.length; i < len; i++){
876                             args[i] = args[i].replace(re, "$1");
877                         }
878                         args = [values[name]].concat(args);
879                     } else {
880                         args = [values[name]];
881                     }
882                     return fm[format].apply(fm, args);
883                 }
884             } else {
885                 return values[name] !== undefined ? values[name] : "";
886             }
887         }
888         return me.html.replace(me.re, fn);
889     },
890
891     /**
892      * Compiles the template into an internal function, eliminating the RegEx overhead.
893      * @return {Ext.Template} this
894      * @hide repeat doc
895      */
896     compile : function(){
897         var me = this,
898             fm = Ext.util.Format,
899             useF = me.disableFormats !== true,
900             sep = Ext.isGecko ? "+" : ",",
901             body;
902
903         function fn(m, name, format, args){
904             if(format && useF){
905                 args = args ? ',' + args : "";
906                 if(format.substr(0, 5) != "this."){
907                     format = "fm." + format + '(';
908                 }else{
909                     format = 'this.call("'+ format.substr(5) + '", ';
910                     args = ", values";
911                 }
912             }else{
913                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
914             }
915             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
916         }
917
918         // branched to use + in gecko and [].join() in others
919         if(Ext.isGecko){
920             body = "this.compiled = function(values){ return '" +
921                    me.html.replace(me.compileARe, '\\\\').replace(me.compileBRe, '\\n').replace(me.compileCRe, "\\'").replace(me.re, fn) +
922                     "';};";
923         }else{
924             body = ["this.compiled = function(values){ return ['"];
925             body.push(me.html.replace(me.compileARe, '\\\\').replace(me.compileBRe, '\\n').replace(me.compileCRe, "\\'").replace(me.re, fn));
926             body.push("'].join('');};");
927             body = body.join('');
928         }
929         eval(body);
930         return me;
931     },
932
933     // private function used to call members
934     call : function(fnName, value, allValues){
935         return this[fnName](value, allValues);
936     }
937 });
938 Ext.Template.prototype.apply = Ext.Template.prototype.applyTemplate;
939 /*
940  * This is code is also distributed under MIT license for use
941  * with jQuery and prototype JavaScript libraries.
942  */
943 /**
944  * @class Ext.DomQuery
945 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).
946 <p>
947 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>
948
949 <p>
950 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.
951 </p>
952 <h4>Element Selectors:</h4>
953 <ul class="list">
954     <li> <b>*</b> any element</li>
955     <li> <b>E</b> an element with the tag E</li>
956     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
957     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
958     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
959     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
960 </ul>
961 <h4>Attribute Selectors:</h4>
962 <p>The use of &#64; and quotes are optional. For example, div[&#64;foo='bar'] is also a valid attribute selector.</p>
963 <ul class="list">
964     <li> <b>E[foo]</b> has an attribute "foo"</li>
965     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
966     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
967     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
968     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
969     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
970     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
971 </ul>
972 <h4>Pseudo Classes:</h4>
973 <ul class="list">
974     <li> <b>E:first-child</b> E is the first child of its parent</li>
975     <li> <b>E:last-child</b> E is the last child of its parent</li>
976     <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>
977     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
978     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
979     <li> <b>E:only-child</b> E is the only child of its parent</li>
980     <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>
981     <li> <b>E:first</b> the first E in the resultset</li>
982     <li> <b>E:last</b> the last E in the resultset</li>
983     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
984     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
985     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
986     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
987     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
988     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
989     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
990     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
991     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
992     <li> <b>E:any(S1|S2|S2)</b> an E element which matches any of the simple selectors S1, S2 or S3//\\</li>
993 </ul>
994 <h4>CSS Value Selectors:</h4>
995 <ul class="list">
996     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
997     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
998     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
999     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
1000     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
1001     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
1002 </ul>
1003  * @singleton
1004  */
1005 Ext.DomQuery = function(){
1006     var cache = {}, 
1007         simpleCache = {}, 
1008         valueCache = {},
1009         nonSpace = /\S/,
1010         trimRe = /^\s+|\s+$/g,
1011         tplRe = /\{(\d+)\}/g,
1012         modeRe = /^(\s?[\/>+~]\s?|\s|$)/,
1013         tagTokenRe = /^(#)?([\w-\*]+)/,
1014         nthRe = /(\d*)n\+?(\d*)/, 
1015         nthRe2 = /\D/,
1016         // This is for IE MSXML which does not support expandos.
1017         // IE runs the same speed using setAttribute, however FF slows way down
1018         // and Safari completely fails so they need to continue to use expandos.
1019         isIE = window.ActiveXObject ? true : false,
1020         key = 30803;
1021     
1022     // this eval is stop the compressor from
1023     // renaming the variable to something shorter
1024     eval("var batch = 30803;");         
1025
1026     // Retrieve the child node from a particular
1027     // parent at the specified index.
1028     function child(parent, index){
1029         var i = 0,
1030             n = parent.firstChild;
1031         while(n){
1032             if(n.nodeType == 1){
1033                if(++i == index){
1034                    return n;
1035                }
1036             }
1037             n = n.nextSibling;
1038         }
1039         return null;
1040     }
1041
1042     // retrieve the next element node
1043     function next(n){   
1044         while((n = n.nextSibling) && n.nodeType != 1);
1045         return n;
1046     }
1047
1048     // retrieve the previous element node 
1049     function prev(n){
1050         while((n = n.previousSibling) && n.nodeType != 1);
1051         return n;
1052     }
1053
1054     // Mark each child node with a nodeIndex skipping and
1055     // removing empty text nodes.
1056     function children(parent){
1057         var n = parent.firstChild,
1058             nodeIndex = -1,
1059             nextNode;
1060         while(n){
1061             nextNode = n.nextSibling;
1062             // clean worthless empty nodes.
1063             if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
1064                 parent.removeChild(n);
1065             }else{
1066                 // add an expando nodeIndex
1067                 n.nodeIndex = ++nodeIndex;
1068             }
1069             n = nextNode;
1070         }
1071         return this;
1072     }
1073
1074
1075     // nodeSet - array of nodes
1076     // cls - CSS Class
1077     function byClassName(nodeSet, cls){
1078         if(!cls){
1079             return nodeSet;
1080         }
1081         var result = [], ri = -1;
1082         for(var i = 0, ci; ci = nodeSet[i]; i++){
1083             if((' '+ci.className+' ').indexOf(cls) != -1){
1084                 result[++ri] = ci;
1085             }
1086         }
1087         return result;
1088     };
1089
1090     function attrValue(n, attr){
1091         // if its an array, use the first node.
1092         if(!n.tagName && typeof n.length != "undefined"){
1093             n = n[0];
1094         }
1095         if(!n){
1096             return null;
1097         }
1098
1099         if(attr == "for"){
1100             return n.htmlFor;
1101         }
1102         if(attr == "class" || attr == "className"){
1103             return n.className;
1104         }
1105         return n.getAttribute(attr) || n[attr];
1106
1107     };
1108
1109
1110     // ns - nodes
1111     // mode - false, /, >, +, ~
1112     // tagName - defaults to "*"
1113     function getNodes(ns, mode, tagName){
1114         var result = [], ri = -1, cs;
1115         if(!ns){
1116             return result;
1117         }
1118         tagName = tagName || "*";
1119         // convert to array
1120         if(typeof ns.getElementsByTagName != "undefined"){
1121             ns = [ns];
1122         }
1123         
1124         // no mode specified, grab all elements by tagName
1125         // at any depth
1126         if(!mode){
1127             for(var i = 0, ni; ni = ns[i]; i++){
1128                 cs = ni.getElementsByTagName(tagName);
1129                 for(var j = 0, ci; ci = cs[j]; j++){
1130                     result[++ri] = ci;
1131                 }
1132             }
1133         // Direct Child mode (/ or >)
1134         // E > F or E/F all direct children elements of E that have the tag     
1135         } else if(mode == "/" || mode == ">"){
1136             var utag = tagName.toUpperCase();
1137             for(var i = 0, ni, cn; ni = ns[i]; i++){
1138                 cn = ni.childNodes;
1139                 for(var j = 0, cj; cj = cn[j]; j++){
1140                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
1141                         result[++ri] = cj;
1142                     }
1143                 }
1144             }
1145         // Immediately Preceding mode (+)
1146         // E + F all elements with the tag F that are immediately preceded by an element with the tag E
1147         }else if(mode == "+"){
1148             var utag = tagName.toUpperCase();
1149             for(var i = 0, n; n = ns[i]; i++){
1150                 while((n = n.nextSibling) && n.nodeType != 1);
1151                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
1152                     result[++ri] = n;
1153                 }
1154             }
1155         // Sibling mode (~)
1156         // E ~ F all elements with the tag F that are preceded by a sibling element with the tag E
1157         }else if(mode == "~"){
1158             var utag = tagName.toUpperCase();
1159             for(var i = 0, n; n = ns[i]; i++){
1160                 while((n = n.nextSibling)){
1161                     if (n.nodeName == utag || n.nodeName == tagName || tagName == '*'){
1162                         result[++ri] = n;
1163                     }
1164                 }
1165             }
1166         }
1167         return result;
1168     }
1169
1170     function concat(a, b){
1171         if(b.slice){
1172             return a.concat(b);
1173         }
1174         for(var i = 0, l = b.length; i < l; i++){
1175             a[a.length] = b[i];
1176         }
1177         return a;
1178     }
1179
1180     function byTag(cs, tagName){
1181         if(cs.tagName || cs == document){
1182             cs = [cs];
1183         }
1184         if(!tagName){
1185             return cs;
1186         }
1187         var result = [], ri = -1;
1188         tagName = tagName.toLowerCase();
1189         for(var i = 0, ci; ci = cs[i]; i++){
1190             if(ci.nodeType == 1 && ci.tagName.toLowerCase() == tagName){
1191                 result[++ri] = ci;
1192             }
1193         }
1194         return result;
1195     }
1196
1197     function byId(cs, id){
1198         if(cs.tagName || cs == document){
1199             cs = [cs];
1200         }
1201         if(!id){
1202             return cs;
1203         }
1204         var result = [], ri = -1;
1205         for(var i = 0, ci; ci = cs[i]; i++){
1206             if(ci && ci.id == id){
1207                 result[++ri] = ci;
1208                 return result;
1209             }
1210         }
1211         return result;
1212     }
1213
1214     // operators are =, !=, ^=, $=, *=, %=, |= and ~=
1215     // custom can be "{"
1216     function byAttribute(cs, attr, value, op, custom){
1217         var result = [], 
1218             ri = -1, 
1219             useGetStyle = custom == "{",            
1220             fn = Ext.DomQuery.operators[op],        
1221             a,
1222             xml,
1223             hasXml;
1224             
1225         for(var i = 0, ci; ci = cs[i]; i++){
1226             // skip non-element nodes.
1227             if(ci.nodeType != 1){
1228                 continue;
1229             }
1230             // only need to do this for the first node
1231             if(!hasXml){
1232                 xml = Ext.DomQuery.isXml(ci);
1233                 hasXml = true;
1234             }
1235             
1236             // we only need to change the property names if we're dealing with html nodes, not XML
1237             if(!xml){
1238                 if(useGetStyle){
1239                     a = Ext.DomQuery.getStyle(ci, attr);
1240                 } else if (attr == "class" || attr == "className"){
1241                     a = ci.className;
1242                 } else if (attr == "for"){
1243                     a = ci.htmlFor;
1244                 } else if (attr == "href"){
1245                     // getAttribute href bug
1246                     // http://www.glennjones.net/Post/809/getAttributehrefbug.htm
1247                     a = ci.getAttribute("href", 2);
1248                 } else{
1249                     a = ci.getAttribute(attr);
1250                 }
1251             }else{
1252                 a = ci.getAttribute(attr);
1253             }
1254             if((fn && fn(a, value)) || (!fn && a)){
1255                 result[++ri] = ci;
1256             }
1257         }
1258         return result;
1259     }
1260
1261     function byPseudo(cs, name, value){
1262         return Ext.DomQuery.pseudos[name](cs, value);
1263     }
1264
1265     function nodupIEXml(cs){
1266         var d = ++key, 
1267             r;
1268         cs[0].setAttribute("_nodup", d);
1269         r = [cs[0]];
1270         for(var i = 1, len = cs.length; i < len; i++){
1271             var c = cs[i];
1272             if(!c.getAttribute("_nodup") != d){
1273                 c.setAttribute("_nodup", d);
1274                 r[r.length] = c;
1275             }
1276         }
1277         for(var i = 0, len = cs.length; i < len; i++){
1278             cs[i].removeAttribute("_nodup");
1279         }
1280         return r;
1281     }
1282
1283     function nodup(cs){
1284         if(!cs){
1285             return [];
1286         }
1287         var len = cs.length, c, i, r = cs, cj, ri = -1;
1288         if(!len || typeof cs.nodeType != "undefined" || len == 1){
1289             return cs;
1290         }
1291         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
1292             return nodupIEXml(cs);
1293         }
1294         var d = ++key;
1295         cs[0]._nodup = d;
1296         for(i = 1; c = cs[i]; i++){
1297             if(c._nodup != d){
1298                 c._nodup = d;
1299             }else{
1300                 r = [];
1301                 for(var j = 0; j < i; j++){
1302                     r[++ri] = cs[j];
1303                 }
1304                 for(j = i+1; cj = cs[j]; j++){
1305                     if(cj._nodup != d){
1306                         cj._nodup = d;
1307                         r[++ri] = cj;
1308                     }
1309                 }
1310                 return r;
1311             }
1312         }
1313         return r;
1314     }
1315
1316     function quickDiffIEXml(c1, c2){
1317         var d = ++key,
1318             r = [];
1319         for(var i = 0, len = c1.length; i < len; i++){
1320             c1[i].setAttribute("_qdiff", d);
1321         }        
1322         for(var i = 0, len = c2.length; i < len; i++){
1323             if(c2[i].getAttribute("_qdiff") != d){
1324                 r[r.length] = c2[i];
1325             }
1326         }
1327         for(var i = 0, len = c1.length; i < len; i++){
1328            c1[i].removeAttribute("_qdiff");
1329         }
1330         return r;
1331     }
1332
1333     function quickDiff(c1, c2){
1334         var len1 = c1.length,
1335                 d = ++key,
1336                 r = [];
1337         if(!len1){
1338             return c2;
1339         }
1340         if(isIE && typeof c1[0].selectSingleNode != "undefined"){
1341             return quickDiffIEXml(c1, c2);
1342         }        
1343         for(var i = 0; i < len1; i++){
1344             c1[i]._qdiff = d;
1345         }        
1346         for(var i = 0, len = c2.length; i < len; i++){
1347             if(c2[i]._qdiff != d){
1348                 r[r.length] = c2[i];
1349             }
1350         }
1351         return r;
1352     }
1353
1354     function quickId(ns, mode, root, id){
1355         if(ns == root){
1356            var d = root.ownerDocument || root;
1357            return d.getElementById(id);
1358         }
1359         ns = getNodes(ns, mode, "*");
1360         return byId(ns, id);
1361     }
1362
1363     return {
1364         getStyle : function(el, name){
1365             return Ext.fly(el).getStyle(name);
1366         },
1367         /**
1368          * Compiles a selector/xpath query into a reusable function. The returned function
1369          * takes one parameter "root" (optional), which is the context node from where the query should start.
1370          * @param {String} selector The selector/xpath query
1371          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
1372          * @return {Function}
1373          */
1374         compile : function(path, type){
1375             type = type || "select";
1376
1377             // setup fn preamble
1378             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"],
1379                         mode,           
1380                         lastPath,
1381                 matchers = Ext.DomQuery.matchers,
1382                 matchersLn = matchers.length,
1383                 modeMatch,
1384                 // accept leading mode switch
1385                 lmode = path.match(modeRe);
1386             
1387             if(lmode && lmode[1]){
1388                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
1389                 path = path.replace(lmode[1], "");
1390             }
1391             
1392             // strip leading slashes
1393             while(path.substr(0, 1)=="/"){
1394                 path = path.substr(1);
1395             }
1396
1397             while(path && lastPath != path){
1398                 lastPath = path;
1399                 var tokenMatch = path.match(tagTokenRe);
1400                 if(type == "select"){
1401                     if(tokenMatch){
1402                         // ID Selector
1403                         if(tokenMatch[1] == "#"){
1404                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tokenMatch[2]+'");';                 
1405                         }else{
1406                             fn[fn.length] = 'n = getNodes(n, mode, "'+tokenMatch[2]+'");';
1407                         }
1408                         path = path.replace(tokenMatch[0], "");
1409                     }else if(path.substr(0, 1) != '@'){
1410                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
1411                     }
1412                 // type of "simple"
1413                 }else{
1414                     if(tokenMatch){
1415                         if(tokenMatch[1] == "#"){
1416                             fn[fn.length] = 'n = byId(n, "'+tokenMatch[2]+'");';
1417                         }else{
1418                             fn[fn.length] = 'n = byTag(n, "'+tokenMatch[2]+'");';
1419                         }
1420                         path = path.replace(tokenMatch[0], "");
1421                     }
1422                 }
1423                 while(!(modeMatch = path.match(modeRe))){
1424                     var matched = false;
1425                     for(var j = 0; j < matchersLn; j++){
1426                         var t = matchers[j];
1427                         var m = path.match(t.re);
1428                         if(m){
1429                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
1430                                 return m[i];
1431                             });
1432                             path = path.replace(m[0], "");
1433                             matched = true;
1434                             break;
1435                         }
1436                     }
1437                     // prevent infinite loop on bad selector
1438                     if(!matched){
1439                         throw 'Error parsing selector, parsing failed at "' + path + '"';
1440                     }
1441                 }
1442                 if(modeMatch[1]){
1443                     fn[fn.length] = 'mode="'+modeMatch[1].replace(trimRe, "")+'";';
1444                     path = path.replace(modeMatch[1], "");
1445                 }
1446             }
1447             // close fn out
1448             fn[fn.length] = "return nodup(n);\n}";
1449             
1450             // eval fn and return it
1451             eval(fn.join(""));
1452             return f;
1453         },
1454
1455         /**
1456          * Selects a group of elements.
1457          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
1458          * @param {Node/String} root (optional) The start of the query (defaults to document).
1459          * @return {Array} An Array of DOM elements which match the selector. If there are
1460          * no matches, and empty Array is returned.
1461          */
1462         jsSelect: function(path, root, type){
1463             // set root to doc if not specified.
1464             root = root || document;
1465             
1466             if(typeof root == "string"){
1467                 root = document.getElementById(root);
1468             }
1469             var paths = path.split(","),
1470                 results = [];
1471                 
1472             // loop over each selector
1473             for(var i = 0, len = paths.length; i < len; i++){           
1474                 var subPath = paths[i].replace(trimRe, "");
1475                 // compile and place in cache
1476                 if(!cache[subPath]){
1477                     cache[subPath] = Ext.DomQuery.compile(subPath);
1478                     if(!cache[subPath]){
1479                         throw subPath + " is not a valid selector";
1480                     }
1481                 }
1482                 var result = cache[subPath](root);
1483                 if(result && result != document){
1484                     results = results.concat(result);
1485                 }
1486             }
1487             
1488             // if there were multiple selectors, make sure dups
1489             // are eliminated
1490             if(paths.length > 1){
1491                 return nodup(results);
1492             }
1493             return results;
1494         },
1495         isXml: function(el) {
1496             var docEl = (el ? el.ownerDocument || el : 0).documentElement;
1497             return docEl ? docEl.nodeName !== "HTML" : false;
1498         },
1499         select : document.querySelectorAll ? function(path, root, type) {
1500             root = root || document;
1501             if (!Ext.DomQuery.isXml(root)) {
1502                 try {
1503                     var cs = root.querySelectorAll(path);
1504                     return Ext.toArray(cs);
1505                 }
1506                 catch (ex) {}           
1507             }       
1508             return Ext.DomQuery.jsSelect.call(this, path, root, type);
1509         } : function(path, root, type) {
1510             return Ext.DomQuery.jsSelect.call(this, path, root, type);
1511         },
1512
1513         /**
1514          * Selects a single element.
1515          * @param {String} selector The selector/xpath query
1516          * @param {Node} root (optional) The start of the query (defaults to document).
1517          * @return {Element} The DOM element which matched the selector.
1518          */
1519         selectNode : function(path, root){
1520             return Ext.DomQuery.select(path, root)[0];
1521         },
1522
1523         /**
1524          * Selects the value of a node, optionally replacing null with the defaultValue.
1525          * @param {String} selector The selector/xpath query
1526          * @param {Node} root (optional) The start of the query (defaults to document).
1527          * @param {String} defaultValue
1528          * @return {String}
1529          */
1530         selectValue : function(path, root, defaultValue){
1531             path = path.replace(trimRe, "");
1532             if(!valueCache[path]){
1533                 valueCache[path] = Ext.DomQuery.compile(path, "select");
1534             }
1535             var n = valueCache[path](root), v;
1536             n = n[0] ? n[0] : n;
1537                     
1538             // overcome a limitation of maximum textnode size
1539             // Rumored to potentially crash IE6 but has not been confirmed.
1540             // http://reference.sitepoint.com/javascript/Node/normalize
1541             // https://developer.mozilla.org/En/DOM/Node.normalize          
1542             if (typeof n.normalize == 'function') n.normalize();
1543             
1544             v = (n && n.firstChild ? n.firstChild.nodeValue : null);
1545             return ((v === null||v === undefined||v==='') ? defaultValue : v);
1546         },
1547
1548         /**
1549          * Selects the value of a node, parsing integers and floats. Returns the defaultValue, or 0 if none is specified.
1550          * @param {String} selector The selector/xpath query
1551          * @param {Node} root (optional) The start of the query (defaults to document).
1552          * @param {Number} defaultValue
1553          * @return {Number}
1554          */
1555         selectNumber : function(path, root, defaultValue){
1556             var v = Ext.DomQuery.selectValue(path, root, defaultValue || 0);
1557             return parseFloat(v);
1558         },
1559
1560         /**
1561          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
1562          * @param {String/HTMLElement/Array} el An element id, element or array of elements
1563          * @param {String} selector The simple selector to test
1564          * @return {Boolean}
1565          */
1566         is : function(el, ss){
1567             if(typeof el == "string"){
1568                 el = document.getElementById(el);
1569             }
1570             var isArray = Ext.isArray(el),
1571                 result = Ext.DomQuery.filter(isArray ? el : [el], ss);
1572             return isArray ? (result.length == el.length) : (result.length > 0);
1573         },
1574
1575         /**
1576          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
1577          * @param {Array} el An array of elements to filter
1578          * @param {String} selector The simple selector to test
1579          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
1580          * the selector instead of the ones that match
1581          * @return {Array} An Array of DOM elements which match the selector. If there are
1582          * no matches, and empty Array is returned.
1583          */
1584         filter : function(els, ss, nonMatches){
1585             ss = ss.replace(trimRe, "");
1586             if(!simpleCache[ss]){
1587                 simpleCache[ss] = Ext.DomQuery.compile(ss, "simple");
1588             }
1589             var result = simpleCache[ss](els);
1590             return nonMatches ? quickDiff(result, els) : result;
1591         },
1592
1593         /**
1594          * Collection of matching regular expressions and code snippets.
1595          * Each capture group within () will be replace the {} in the select
1596          * statement as specified by their index.
1597          */
1598         matchers : [{
1599                 re: /^\.([\w-]+)/,
1600                 select: 'n = byClassName(n, " {1} ");'
1601             }, {
1602                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
1603                 select: 'n = byPseudo(n, "{1}", "{2}");'
1604             },{
1605                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
1606                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
1607             }, {
1608                 re: /^#([\w-]+)/,
1609                 select: 'n = byId(n, "{1}");'
1610             },{
1611                 re: /^@([\w-]+)/,
1612                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
1613             }
1614         ],
1615
1616         /**
1617          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
1618          * 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;.
1619          */
1620         operators : {
1621             "=" : function(a, v){
1622                 return a == v;
1623             },
1624             "!=" : function(a, v){
1625                 return a != v;
1626             },
1627             "^=" : function(a, v){
1628                 return a && a.substr(0, v.length) == v;
1629             },
1630             "$=" : function(a, v){
1631                 return a && a.substr(a.length-v.length) == v;
1632             },
1633             "*=" : function(a, v){
1634                 return a && a.indexOf(v) !== -1;
1635             },
1636             "%=" : function(a, v){
1637                 return (a % v) == 0;
1638             },
1639             "|=" : function(a, v){
1640                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
1641             },
1642             "~=" : function(a, v){
1643                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
1644             }
1645         },
1646
1647         /**
1648          * <p>Object hash of "pseudo class" filter functions which are used when filtering selections. Each function is passed
1649          * two parameters:</p><div class="mdetail-params"><ul>
1650          * <li><b>c</b> : Array<div class="sub-desc">An Array of DOM elements to filter.</div></li>
1651          * <li><b>v</b> : String<div class="sub-desc">The argument (if any) supplied in the selector.</div></li>
1652          * </ul></div>
1653          * <p>A filter function returns an Array of DOM elements which conform to the pseudo class.</p>
1654          * <p>In addition to the provided pseudo classes listed above such as <code>first-child</code> and <code>nth-child</code>,
1655          * developers may add additional, custom psuedo class filters to select elements according to application-specific requirements.</p>
1656          * <p>For example, to filter <code>&lt;a></code> elements to only return links to <i>external</i> resources:</p>
1657          * <code><pre>
1658 Ext.DomQuery.pseudos.external = function(c, v){
1659     var r = [], ri = -1;
1660     for(var i = 0, ci; ci = c[i]; i++){
1661 //      Include in result set only if it's a link to an external resource
1662         if(ci.hostname != location.hostname){
1663             r[++ri] = ci;
1664         }
1665     }
1666     return r;
1667 };</pre></code>
1668          * Then external links could be gathered with the following statement:<code><pre>
1669 var externalLinks = Ext.select("a:external");
1670 </code></pre>
1671          */
1672         pseudos : {
1673             "first-child" : function(c){
1674                 var r = [], ri = -1, n;
1675                 for(var i = 0, ci; ci = n = c[i]; i++){
1676                     while((n = n.previousSibling) && n.nodeType != 1);
1677                     if(!n){
1678                         r[++ri] = ci;
1679                     }
1680                 }
1681                 return r;
1682             },
1683
1684             "last-child" : function(c){
1685                 var r = [], ri = -1, n;
1686                 for(var i = 0, ci; ci = n = c[i]; i++){
1687                     while((n = n.nextSibling) && n.nodeType != 1);
1688                     if(!n){
1689                         r[++ri] = ci;
1690                     }
1691                 }
1692                 return r;
1693             },
1694
1695             "nth-child" : function(c, a) {
1696                 var r = [], ri = -1,
1697                         m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a),
1698                         f = (m[1] || 1) - 0, l = m[2] - 0;
1699                 for(var i = 0, n; n = c[i]; i++){
1700                     var pn = n.parentNode;
1701                     if (batch != pn._batch) {
1702                         var j = 0;
1703                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
1704                             if(cn.nodeType == 1){
1705                                cn.nodeIndex = ++j;
1706                             }
1707                         }
1708                         pn._batch = batch;
1709                     }
1710                     if (f == 1) {
1711                         if (l == 0 || n.nodeIndex == l){
1712                             r[++ri] = n;
1713                         }
1714                     } else if ((n.nodeIndex + l) % f == 0){
1715                         r[++ri] = n;
1716                     }
1717                 }
1718
1719                 return r;
1720             },
1721
1722             "only-child" : function(c){
1723                 var r = [], ri = -1;;
1724                 for(var i = 0, ci; ci = c[i]; i++){
1725                     if(!prev(ci) && !next(ci)){
1726                         r[++ri] = ci;
1727                     }
1728                 }
1729                 return r;
1730             },
1731
1732             "empty" : function(c){
1733                 var r = [], ri = -1;
1734                 for(var i = 0, ci; ci = c[i]; i++){
1735                     var cns = ci.childNodes, j = 0, cn, empty = true;
1736                     while(cn = cns[j]){
1737                         ++j;
1738                         if(cn.nodeType == 1 || cn.nodeType == 3){
1739                             empty = false;
1740                             break;
1741                         }
1742                     }
1743                     if(empty){
1744                         r[++ri] = ci;
1745                     }
1746                 }
1747                 return r;
1748             },
1749
1750             "contains" : function(c, v){
1751                 var r = [], ri = -1;
1752                 for(var i = 0, ci; ci = c[i]; i++){
1753                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
1754                         r[++ri] = ci;
1755                     }
1756                 }
1757                 return r;
1758             },
1759
1760             "nodeValue" : function(c, v){
1761                 var r = [], ri = -1;
1762                 for(var i = 0, ci; ci = c[i]; i++){
1763                     if(ci.firstChild && ci.firstChild.nodeValue == v){
1764                         r[++ri] = ci;
1765                     }
1766                 }
1767                 return r;
1768             },
1769
1770             "checked" : function(c){
1771                 var r = [], ri = -1;
1772                 for(var i = 0, ci; ci = c[i]; i++){
1773                     if(ci.checked == true){
1774                         r[++ri] = ci;
1775                     }
1776                 }
1777                 return r;
1778             },
1779
1780             "not" : function(c, ss){
1781                 return Ext.DomQuery.filter(c, ss, true);
1782             },
1783
1784             "any" : function(c, selectors){
1785                 var ss = selectors.split('|'),
1786                         r = [], ri = -1, s;
1787                 for(var i = 0, ci; ci = c[i]; i++){
1788                     for(var j = 0; s = ss[j]; j++){
1789                         if(Ext.DomQuery.is(ci, s)){
1790                             r[++ri] = ci;
1791                             break;
1792                         }
1793                     }
1794                 }
1795                 return r;
1796             },
1797
1798             "odd" : function(c){
1799                 return this["nth-child"](c, "odd");
1800             },
1801
1802             "even" : function(c){
1803                 return this["nth-child"](c, "even");
1804             },
1805
1806             "nth" : function(c, a){
1807                 return c[a-1] || [];
1808             },
1809
1810             "first" : function(c){
1811                 return c[0] || [];
1812             },
1813
1814             "last" : function(c){
1815                 return c[c.length-1] || [];
1816             },
1817
1818             "has" : function(c, ss){
1819                 var s = Ext.DomQuery.select,
1820                         r = [], ri = -1;
1821                 for(var i = 0, ci; ci = c[i]; i++){
1822                     if(s(ss, ci).length > 0){
1823                         r[++ri] = ci;
1824                     }
1825                 }
1826                 return r;
1827             },
1828
1829             "next" : function(c, ss){
1830                 var is = Ext.DomQuery.is,
1831                         r = [], ri = -1;
1832                 for(var i = 0, ci; ci = c[i]; i++){
1833                     var n = next(ci);
1834                     if(n && is(n, ss)){
1835                         r[++ri] = ci;
1836                     }
1837                 }
1838                 return r;
1839             },
1840
1841             "prev" : function(c, ss){
1842                 var is = Ext.DomQuery.is,
1843                         r = [], ri = -1;
1844                 for(var i = 0, ci; ci = c[i]; i++){
1845                     var n = prev(ci);
1846                     if(n && is(n, ss)){
1847                         r[++ri] = ci;
1848                     }
1849                 }
1850                 return r;
1851             }
1852         }
1853     };
1854 }();
1855
1856 /**
1857  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Ext.DomQuery#select}
1858  * @param {String} path The selector/xpath query
1859  * @param {Node} root (optional) The start of the query (defaults to document).
1860  * @return {Array}
1861  * @member Ext
1862  * @method query
1863  */
1864 Ext.query = Ext.DomQuery.select;
1865 /**
1866  * @class Ext.util.DelayedTask
1867  * <p> The DelayedTask class provides a convenient way to "buffer" the execution of a method,
1868  * performing setTimeout where a new timeout cancels the old timeout. When called, the
1869  * task will wait the specified time period before executing. If durng that time period,
1870  * the task is called again, the original call will be cancelled. This continues so that
1871  * the function is only called a single time for each iteration.</p>
1872  * <p>This method is especially useful for things like detecting whether a user has finished
1873  * typing in a text field. An example would be performing validation on a keypress. You can
1874  * use this class to buffer the keypress events for a certain number of milliseconds, and
1875  * perform only if they stop for that amount of time.  Usage:</p><pre><code>
1876 var task = new Ext.util.DelayedTask(function(){
1877     alert(Ext.getDom('myInputField').value.length);
1878 });
1879 // Wait 500ms before calling our function. If the user presses another key 
1880 // during that 500ms, it will be cancelled and we'll wait another 500ms.
1881 Ext.get('myInputField').on('keypress', function(){
1882     task.{@link #delay}(500); 
1883 });
1884  * </code></pre> 
1885  * <p>Note that we are using a DelayedTask here to illustrate a point. The configuration
1886  * option <tt>buffer</tt> for {@link Ext.util.Observable#addListener addListener/on} will
1887  * also setup a delayed task for you to buffer events.</p> 
1888  * @constructor The parameters to this constructor serve as defaults and are not required.
1889  * @param {Function} fn (optional) The default function to call.
1890  * @param {Object} scope The default scope (The <code><b>this</b></code> reference) in which the
1891  * function is called. If not specified, <code>this</code> will refer to the browser window.
1892  * @param {Array} args (optional) The default Array of arguments.
1893  */
1894 Ext.util.DelayedTask = function(fn, scope, args){
1895     var me = this,
1896         id,     
1897         call = function(){
1898                 clearInterval(id);
1899                 id = null;
1900                 fn.apply(scope, args || []);
1901             };
1902             
1903     /**
1904      * Cancels any pending timeout and queues a new one
1905      * @param {Number} delay The milliseconds to delay
1906      * @param {Function} newFn (optional) Overrides function passed to constructor
1907      * @param {Object} newScope (optional) Overrides scope passed to constructor. Remember that if no scope
1908      * is specified, <code>this</code> will refer to the browser window.
1909      * @param {Array} newArgs (optional) Overrides args passed to constructor
1910      */
1911     me.delay = function(delay, newFn, newScope, newArgs){
1912         me.cancel();
1913         fn = newFn || fn;
1914         scope = newScope || scope;
1915         args = newArgs || args;
1916         id = setInterval(call, delay);
1917     };
1918
1919     /**
1920      * Cancel the last queued timeout
1921      */
1922     me.cancel = function(){
1923         if(id){
1924             clearInterval(id);
1925             id = null;
1926         }
1927     };
1928 };(function(){
1929
1930 var EXTUTIL = Ext.util,
1931     EACH = Ext.each,
1932     TRUE = true,
1933     FALSE = false;
1934 /**
1935  * @class Ext.util.Observable
1936  * Base class that provides a common interface for publishing events. Subclasses are expected to
1937  * to have a property "events" with all the events defined, and, optionally, a property "listeners"
1938  * with configured listeners defined.<br>
1939  * For example:
1940  * <pre><code>
1941 Employee = Ext.extend(Ext.util.Observable, {
1942     constructor: function(config){
1943         this.name = config.name;
1944         this.addEvents({
1945             "fired" : true,
1946             "quit" : true
1947         });
1948
1949         // Copy configured listeners into *this* object so that the base class&#39;s
1950         // constructor will add them.
1951         this.listeners = config.listeners;
1952
1953         // Call our superclass constructor to complete construction process.
1954         Employee.superclass.constructor.call(this, config)
1955     }
1956 });
1957 </code></pre>
1958  * This could then be used like this:<pre><code>
1959 var newEmployee = new Employee({
1960     name: employeeName,
1961     listeners: {
1962         quit: function() {
1963             // By default, "this" will be the object that fired the event.
1964             alert(this.name + " has quit!");
1965         }
1966     }
1967 });
1968 </code></pre>
1969  */
1970 EXTUTIL.Observable = function(){
1971     /**
1972      * @cfg {Object} listeners (optional) <p>A config object containing one or more event handlers to be added to this
1973      * object during initialization.  This should be a valid listeners config object as specified in the
1974      * {@link #addListener} example for attaching multiple handlers at once.</p>
1975      * <br><p><b><u>DOM events from ExtJs {@link Ext.Component Components}</u></b></p>
1976      * <br><p>While <i>some</i> ExtJs Component classes export selected DOM events (e.g. "click", "mouseover" etc), this
1977      * is usually only done when extra value can be added. For example the {@link Ext.DataView DataView}'s
1978      * <b><code>{@link Ext.DataView#click click}</code></b> event passing the node clicked on. To access DOM
1979      * events directly from a Component's HTMLElement, listeners must be added to the <i>{@link Ext.Component#getEl Element}</i> after the Component
1980      * has been rendered. A plugin can simplify this step:<pre><code>
1981 // Plugin is configured with a listeners config object.
1982 // The Component is appended to the argument list of all handler functions.
1983 Ext.DomObserver = Ext.extend(Object, {
1984     constructor: function(config) {
1985         this.listeners = config.listeners ? config.listeners : config;
1986     },
1987
1988     // Component passes itself into plugin&#39;s init method
1989     init: function(c) {
1990         var p, l = this.listeners;
1991         for (p in l) {
1992             if (Ext.isFunction(l[p])) {
1993                 l[p] = this.createHandler(l[p], c);
1994             } else {
1995                 l[p].fn = this.createHandler(l[p].fn, c);
1996             }
1997         }
1998
1999         // Add the listeners to the Element immediately following the render call
2000         c.render = c.render.{@link Function#createSequence createSequence}(function() {
2001             var e = c.getEl();
2002             if (e) {
2003                 e.on(l);
2004             }
2005         });
2006     },
2007
2008     createHandler: function(fn, c) {
2009         return function(e) {
2010             fn.call(this, e, c);
2011         };
2012     }
2013 });
2014
2015 var combo = new Ext.form.ComboBox({
2016
2017     // Collapse combo when its element is clicked on
2018     plugins: [ new Ext.DomObserver({
2019         click: function(evt, comp) {
2020             comp.collapse();
2021         }
2022     })],
2023     store: myStore,
2024     typeAhead: true,
2025     mode: 'local',
2026     triggerAction: 'all'
2027 });
2028      * </code></pre></p>
2029      */
2030     var me = this, e = me.events;
2031     if(me.listeners){
2032         me.on(me.listeners);
2033         delete me.listeners;
2034     }
2035     me.events = e || {};
2036 };
2037
2038 EXTUTIL.Observable.prototype = {
2039     // private
2040     filterOptRe : /^(?:scope|delay|buffer|single)$/,
2041
2042     /**
2043      * <p>Fires the specified event with the passed parameters (minus the event name).</p>
2044      * <p>An event may be set to bubble up an Observable parent hierarchy (See {@link Ext.Component#getBubbleTarget})
2045      * by calling {@link #enableBubble}.</p>
2046      * @param {String} eventName The name of the event to fire.
2047      * @param {Object...} args Variable number of parameters are passed to handlers.
2048      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true.
2049      */
2050     fireEvent : function(){
2051         var a = Array.prototype.slice.call(arguments, 0),
2052             ename = a[0].toLowerCase(),
2053             me = this,
2054             ret = TRUE,
2055             ce = me.events[ename],
2056             cc,
2057             q,
2058             c;
2059         if (me.eventsSuspended === TRUE) {
2060             if (q = me.eventQueue) {
2061                 q.push(a);
2062             }
2063         }
2064         else if(typeof ce == 'object') {
2065             if (ce.bubble){
2066                 if(ce.fire.apply(ce, a.slice(1)) === FALSE) {
2067                     return FALSE;
2068                 }
2069                 c = me.getBubbleTarget && me.getBubbleTarget();
2070                 if(c && c.enableBubble) {
2071                     cc = c.events[ename];
2072                     if(!cc || typeof cc != 'object' || !cc.bubble) {
2073                         c.enableBubble(ename);
2074                     }
2075                     return c.fireEvent.apply(c, a);
2076                 }
2077             }
2078             else {
2079                 a.shift();
2080                 ret = ce.fire.apply(ce, a);
2081             }
2082         }
2083         return ret;
2084     },
2085
2086     /**
2087      * Appends an event handler to this object.
2088      * @param {String}   eventName The name of the event to listen for.
2089      * @param {Function} handler The method the event invokes.
2090      * @param {Object}   scope (optional) The scope (<code><b>this</b></code> reference) in which the handler function is executed.
2091      * <b>If omitted, defaults to the object which fired the event.</b>
2092      * @param {Object}   options (optional) An object containing handler configuration.
2093      * properties. This may contain any of the following properties:<ul>
2094      * <li><b>scope</b> : Object<div class="sub-desc">The scope (<code><b>this</b></code> reference) in which the handler function is executed.
2095      * <b>If omitted, defaults to the object which fired the event.</b></div></li>
2096      * <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>
2097      * <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>
2098      * <li><b>buffer</b> : Number<div class="sub-desc">Causes the handler to be scheduled to run in an {@link Ext.util.DelayedTask} delayed
2099      * by the specified number of milliseconds. If the event fires again within that time, the original
2100      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</div></li>
2101      * <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>
2102      * if the event was bubbled up from a child Observable.</div></li>
2103      * </ul><br>
2104      * <p>
2105      * <b>Combining Options</b><br>
2106      * Using the options argument, it is possible to combine different types of listeners:<br>
2107      * <br>
2108      * A delayed, one-time listener.
2109      * <pre><code>
2110 myDataView.on('click', this.onClick, this, {
2111 single: true,
2112 delay: 100
2113 });</code></pre>
2114      * <p>
2115      * <b>Attaching multiple handlers in 1 call</b><br>
2116      * The method also allows for a single argument to be passed which is a config object containing properties
2117      * which specify multiple handlers.
2118      * <p>
2119      * <pre><code>
2120 myGridPanel.on({
2121 'click' : {
2122     fn: this.onClick,
2123     scope: this,
2124     delay: 100
2125 },
2126 'mouseover' : {
2127     fn: this.onMouseOver,
2128     scope: this
2129 },
2130 'mouseout' : {
2131     fn: this.onMouseOut,
2132     scope: this
2133 }
2134 });</code></pre>
2135  * <p>
2136  * Or a shorthand syntax:<br>
2137  * <pre><code>
2138 myGridPanel.on({
2139 'click' : this.onClick,
2140 'mouseover' : this.onMouseOver,
2141 'mouseout' : this.onMouseOut,
2142  scope: this
2143 });</code></pre>
2144      */
2145     addListener : function(eventName, fn, scope, o){
2146         var me = this,
2147             e,
2148             oe,
2149             ce;
2150             
2151         if (typeof eventName == 'object') {
2152             o = eventName;
2153             for (e in o) {
2154                 oe = o[e];
2155                 if (!me.filterOptRe.test(e)) {
2156                     me.addListener(e, oe.fn || oe, oe.scope || o.scope, oe.fn ? oe : o);
2157                 }
2158             }
2159         } else {
2160             eventName = eventName.toLowerCase();
2161             ce = me.events[eventName] || TRUE;
2162             if (typeof ce == 'boolean') {
2163                 me.events[eventName] = ce = new EXTUTIL.Event(me, eventName);
2164             }
2165             ce.addListener(fn, scope, typeof o == 'object' ? o : {});
2166         }
2167     },
2168
2169     /**
2170      * Removes an event handler.
2171      * @param {String}   eventName The type of event the handler was associated with.
2172      * @param {Function} handler   The handler to remove. <b>This must be a reference to the function passed into the {@link #addListener} call.</b>
2173      * @param {Object}   scope     (optional) The scope originally specified for the handler.
2174      */
2175     removeListener : function(eventName, fn, scope){
2176         var ce = this.events[eventName.toLowerCase()];
2177         if (typeof ce == 'object') {
2178             ce.removeListener(fn, scope);
2179         }
2180     },
2181
2182     /**
2183      * Removes all listeners for this object
2184      */
2185     purgeListeners : function(){
2186         var events = this.events,
2187             evt,
2188             key;
2189         for(key in events){
2190             evt = events[key];
2191             if(typeof evt == 'object'){
2192                 evt.clearListeners();
2193             }
2194         }
2195     },
2196
2197     /**
2198      * Adds the specified events to the list of events which this Observable may fire.
2199      * @param {Object|String} o Either an object with event names as properties with a value of <code>true</code>
2200      * or the first event name string if multiple event names are being passed as separate parameters.
2201      * @param {string} Optional. Event name if multiple event names are being passed as separate parameters.
2202      * Usage:<pre><code>
2203 this.addEvents('storeloaded', 'storecleared');
2204 </code></pre>
2205      */
2206     addEvents : function(o){
2207         var me = this;
2208         me.events = me.events || {};
2209         if (typeof o == 'string') {
2210             var a = arguments,
2211                 i = a.length;
2212             while(i--) {
2213                 me.events[a[i]] = me.events[a[i]] || TRUE;
2214             }
2215         } else {
2216             Ext.applyIf(me.events, o);
2217         }
2218     },
2219
2220     /**
2221      * Checks to see if this object has any listeners for a specified event
2222      * @param {String} eventName The name of the event to check for
2223      * @return {Boolean} True if the event is being listened for, else false
2224      */
2225     hasListener : function(eventName){
2226         var e = this.events[eventName.toLowerCase()];
2227         return typeof e == 'object' && e.listeners.length > 0;
2228     },
2229
2230     /**
2231      * Suspend the firing of all events. (see {@link #resumeEvents})
2232      * @param {Boolean} queueSuspended Pass as true to queue up suspended events to be fired
2233      * after the {@link #resumeEvents} call instead of discarding all suspended events;
2234      */
2235     suspendEvents : function(queueSuspended){
2236         this.eventsSuspended = TRUE;
2237         if(queueSuspended && !this.eventQueue){
2238             this.eventQueue = [];
2239         }
2240     },
2241
2242     /**
2243      * Resume firing events. (see {@link #suspendEvents})
2244      * If events were suspended using the <tt><b>queueSuspended</b></tt> parameter, then all
2245      * events fired during event suspension will be sent to any listeners now.
2246      */
2247     resumeEvents : function(){
2248         var me = this,
2249             queued = me.eventQueue || [];
2250         me.eventsSuspended = FALSE;
2251         delete me.eventQueue;
2252         EACH(queued, function(e) {
2253             me.fireEvent.apply(me, e);
2254         });
2255     }
2256 };
2257
2258 var OBSERVABLE = EXTUTIL.Observable.prototype;
2259 /**
2260  * Appends an event handler to this object (shorthand for {@link #addListener}.)
2261  * @param {String}   eventName     The type of event to listen for
2262  * @param {Function} handler       The method the event invokes
2263  * @param {Object}   scope         (optional) The scope (<code><b>this</b></code> reference) in which the handler function is executed.
2264  * <b>If omitted, defaults to the object which fired the event.</b>
2265  * @param {Object}   options       (optional) An object containing handler configuration.
2266  * @method
2267  */
2268 OBSERVABLE.on = OBSERVABLE.addListener;
2269 /**
2270  * Removes an event handler (shorthand for {@link #removeListener}.)
2271  * @param {String}   eventName     The type of event the handler was associated with.
2272  * @param {Function} handler       The handler to remove. <b>This must be a reference to the function passed into the {@link #addListener} call.</b>
2273  * @param {Object}   scope         (optional) The scope originally specified for the handler.
2274  * @method
2275  */
2276 OBSERVABLE.un = OBSERVABLE.removeListener;
2277
2278 /**
2279  * Removes <b>all</b> added captures from the Observable.
2280  * @param {Observable} o The Observable to release
2281  * @static
2282  */
2283 EXTUTIL.Observable.releaseCapture = function(o){
2284     o.fireEvent = OBSERVABLE.fireEvent;
2285 };
2286
2287 function createTargeted(h, o, scope){
2288     return function(){
2289         if(o.target == arguments[0]){
2290             h.apply(scope, Array.prototype.slice.call(arguments, 0));
2291         }
2292     };
2293 };
2294
2295 function createBuffered(h, o, l, scope){
2296     l.task = new EXTUTIL.DelayedTask();
2297     return function(){
2298         l.task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
2299     };
2300 };
2301
2302 function createSingle(h, e, fn, scope){
2303     return function(){
2304         e.removeListener(fn, scope);
2305         return h.apply(scope, arguments);
2306     };
2307 };
2308
2309 function createDelayed(h, o, l, scope){
2310     return function(){
2311         var task = new EXTUTIL.DelayedTask();
2312         if(!l.tasks) {
2313             l.tasks = [];
2314         }
2315         l.tasks.push(task);
2316         task.delay(o.delay || 10, h, scope, Array.prototype.slice.call(arguments, 0));
2317     };
2318 };
2319
2320 EXTUTIL.Event = function(obj, name){
2321     this.name = name;
2322     this.obj = obj;
2323     this.listeners = [];
2324 };
2325
2326 EXTUTIL.Event.prototype = {
2327     addListener : function(fn, scope, options){
2328         var me = this,
2329             l;
2330         scope = scope || me.obj;
2331         if(!me.isListening(fn, scope)){
2332             l = me.createListener(fn, scope, options);
2333             if(me.firing){ // if we are currently firing this event, don't disturb the listener loop
2334                 me.listeners = me.listeners.slice(0);
2335             }
2336             me.listeners.push(l);
2337         }
2338     },
2339
2340     createListener: function(fn, scope, o){
2341         o = o || {};
2342         scope = scope || this.obj;
2343         var l = {
2344             fn: fn,
2345             scope: scope,
2346             options: o
2347         }, h = fn;
2348         if(o.target){
2349             h = createTargeted(h, o, scope);
2350         }
2351         if(o.delay){
2352             h = createDelayed(h, o, l, scope);
2353         }
2354         if(o.single){
2355             h = createSingle(h, this, fn, scope);
2356         }
2357         if(o.buffer){
2358             h = createBuffered(h, o, l, scope);
2359         }
2360         l.fireFn = h;
2361         return l;
2362     },
2363
2364     findListener : function(fn, scope){
2365         var list = this.listeners,
2366             i = list.length,
2367             l;
2368
2369         scope = scope || this.obj;
2370         while(i--){
2371             l = list[i];
2372             if(l){
2373                 if(l.fn == fn && l.scope == scope){
2374                     return i;
2375                 }
2376             }
2377         }
2378         return -1;
2379     },
2380
2381     isListening : function(fn, scope){
2382         return this.findListener(fn, scope) != -1;
2383     },
2384
2385     removeListener : function(fn, scope){
2386         var index,
2387             l,
2388             k,
2389             me = this,
2390             ret = FALSE;
2391         if((index = me.findListener(fn, scope)) != -1){
2392             if (me.firing) {
2393                 me.listeners = me.listeners.slice(0);
2394             }
2395             l = me.listeners[index];
2396             if(l.task) {
2397                 l.task.cancel();
2398                 delete l.task;
2399             }
2400             k = l.tasks && l.tasks.length;
2401             if(k) {
2402                 while(k--) {
2403                     l.tasks[k].cancel();
2404                 }
2405                 delete l.tasks;
2406             }
2407             me.listeners.splice(index, 1);
2408             ret = TRUE;
2409         }
2410         return ret;
2411     },
2412
2413     // Iterate to stop any buffered/delayed events
2414     clearListeners : function(){
2415         var me = this,
2416             l = me.listeners,
2417             i = l.length;
2418         while(i--) {
2419             me.removeListener(l[i].fn, l[i].scope);
2420         }
2421     },
2422
2423     fire : function(){
2424         var me = this,
2425             listeners = me.listeners,
2426             len = listeners.length,
2427             i = 0,
2428             l;
2429
2430         if(len > 0){
2431             me.firing = TRUE;
2432             var args = Array.prototype.slice.call(arguments, 0);
2433             for (; i < len; i++) {
2434                 l = listeners[i];
2435                 if(l && l.fireFn.apply(l.scope || me.obj || window, args) === FALSE) {
2436                     return (me.firing = FALSE);
2437                 }
2438             }
2439         }
2440         me.firing = FALSE;
2441         return TRUE;
2442     }
2443
2444 };
2445 })();
2446 /**
2447  * @class Ext.util.Observable
2448  */
2449 Ext.apply(Ext.util.Observable.prototype, function(){
2450     // this is considered experimental (along with beforeMethod, afterMethod, removeMethodListener?)
2451     // allows for easier interceptor and sequences, including cancelling and overwriting the return value of the call
2452     // private
2453     function getMethodEvent(method){
2454         var e = (this.methodEvents = this.methodEvents ||
2455         {})[method], returnValue, v, cancel, obj = this;
2456
2457         if (!e) {
2458             this.methodEvents[method] = e = {};
2459             e.originalFn = this[method];
2460             e.methodName = method;
2461             e.before = [];
2462             e.after = [];
2463
2464             var makeCall = function(fn, scope, args){
2465                 if((v = fn.apply(scope || obj, args)) !== undefined){
2466                     if (typeof v == 'object') {
2467                         if(v.returnValue !== undefined){
2468                             returnValue = v.returnValue;
2469                         }else{
2470                             returnValue = v;
2471                         }
2472                         cancel = !!v.cancel;
2473                     }
2474                     else
2475                         if (v === false) {
2476                             cancel = true;
2477                         }
2478                         else {
2479                             returnValue = v;
2480                         }
2481                 }
2482             };
2483
2484             this[method] = function(){
2485                 var args = Array.prototype.slice.call(arguments, 0),
2486                     b;
2487                 returnValue = v = undefined;
2488                 cancel = false;
2489
2490                 for(var i = 0, len = e.before.length; i < len; i++){
2491                     b = e.before[i];
2492                     makeCall(b.fn, b.scope, args);
2493                     if (cancel) {
2494                         return returnValue;
2495                     }
2496                 }
2497
2498                 if((v = e.originalFn.apply(obj, args)) !== undefined){
2499                     returnValue = v;
2500                 }
2501
2502                 for(var i = 0, len = e.after.length; i < len; i++){
2503                     b = e.after[i];
2504                     makeCall(b.fn, b.scope, args);
2505                     if (cancel) {
2506                         return returnValue;
2507                     }
2508                 }
2509                 return returnValue;
2510             };
2511         }
2512         return e;
2513     }
2514
2515     return {
2516         // these are considered experimental
2517         // allows for easier interceptor and sequences, including cancelling and overwriting the return value of the call
2518         // adds an 'interceptor' called before the original method
2519         beforeMethod : function(method, fn, scope){
2520             getMethodEvent.call(this, method).before.push({
2521                 fn: fn,
2522                 scope: scope
2523             });
2524         },
2525
2526         // adds a 'sequence' called after the original method
2527         afterMethod : function(method, fn, scope){
2528             getMethodEvent.call(this, method).after.push({
2529                 fn: fn,
2530                 scope: scope
2531             });
2532         },
2533
2534         removeMethodListener: function(method, fn, scope){
2535             var e = this.getMethodEvent(method);
2536             for(var i = 0, len = e.before.length; i < len; i++){
2537                 if(e.before[i].fn == fn && e.before[i].scope == scope){
2538                     e.before.splice(i, 1);
2539                     return;
2540                 }
2541             }
2542             for(var i = 0, len = e.after.length; i < len; i++){
2543                 if(e.after[i].fn == fn && e.after[i].scope == scope){
2544                     e.after.splice(i, 1);
2545                     return;
2546                 }
2547             }
2548         },
2549
2550         /**
2551          * Relays selected events from the specified Observable as if the events were fired by <tt><b>this</b></tt>.
2552          * @param {Object} o The Observable whose events this object is to relay.
2553          * @param {Array} events Array of event names to relay.
2554          */
2555         relayEvents : function(o, events){
2556             var me = this;
2557             function createHandler(ename){
2558                 return function(){
2559                     return me.fireEvent.apply(me, [ename].concat(Array.prototype.slice.call(arguments, 0)));
2560                 };
2561             }
2562             for(var i = 0, len = events.length; i < len; i++){
2563                 var ename = events[i];
2564                 me.events[ename] = me.events[ename] || true;
2565                 o.on(ename, createHandler(ename), me);
2566             }
2567         },
2568
2569         /**
2570          * <p>Enables events fired by this Observable to bubble up an owner hierarchy by calling
2571          * <code>this.getBubbleTarget()</code> if present. There is no implementation in the Observable base class.</p>
2572          * <p>This is commonly used by Ext.Components to bubble events to owner Containers. See {@link Ext.Component.getBubbleTarget}. The default
2573          * implementation in Ext.Component returns the Component's immediate owner. But if a known target is required, this can be overridden to
2574          * access the required target more quickly.</p>
2575          * <p>Example:</p><pre><code>
2576 Ext.override(Ext.form.Field, {
2577     //  Add functionality to Field&#39;s initComponent to enable the change event to bubble
2578     initComponent : Ext.form.Field.prototype.initComponent.createSequence(function() {
2579         this.enableBubble('change');
2580     }),
2581
2582     //  We know that we want Field&#39;s events to bubble directly to the FormPanel.
2583     getBubbleTarget : function() {
2584         if (!this.formPanel) {
2585             this.formPanel = this.findParentByType('form');
2586         }
2587         return this.formPanel;
2588     }
2589 });
2590
2591 var myForm = new Ext.formPanel({
2592     title: 'User Details',
2593     items: [{
2594         ...
2595     }],
2596     listeners: {
2597         change: function() {
2598             // Title goes red if form has been modified.
2599             myForm.header.setStyle('color', 'red');
2600         }
2601     }
2602 });
2603 </code></pre>
2604          * @param {String/Array} events The event name to bubble, or an Array of event names.
2605          */
2606         enableBubble : function(events){
2607             var me = this;
2608             if(!Ext.isEmpty(events)){
2609                 events = Ext.isArray(events) ? events : Array.prototype.slice.call(arguments, 0);
2610                 for(var i = 0, len = events.length; i < len; i++){
2611                     var ename = events[i];
2612                     ename = ename.toLowerCase();
2613                     var ce = me.events[ename] || true;
2614                     if (typeof ce == 'boolean') {
2615                         ce = new Ext.util.Event(me, ename);
2616                         me.events[ename] = ce;
2617                     }
2618                     ce.bubble = true;
2619                 }
2620             }
2621         }
2622     };
2623 }());
2624
2625
2626 /**
2627  * Starts capture on the specified Observable. All events will be passed
2628  * to the supplied function with the event name + standard signature of the event
2629  * <b>before</b> the event is fired. If the supplied function returns false,
2630  * the event will not fire.
2631  * @param {Observable} o The Observable to capture events from.
2632  * @param {Function} fn The function to call when an event is fired.
2633  * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the Observable firing the event.
2634  * @static
2635  */
2636 Ext.util.Observable.capture = function(o, fn, scope){
2637     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
2638 };
2639
2640
2641 /**
2642  * Sets observability on the passed class constructor.<p>
2643  * <p>This makes any event fired on any instance of the passed class also fire a single event through
2644  * the <i>class</i> allowing for central handling of events on many instances at once.</p>
2645  * <p>Usage:</p><pre><code>
2646 Ext.util.Observable.observeClass(Ext.data.Connection);
2647 Ext.data.Connection.on('beforerequest', function(con, options) {
2648     console.log('Ajax request made to ' + options.url);
2649 });</code></pre>
2650  * @param {Function} c The class constructor to make observable.
2651  * @param {Object} listeners An object containing a series of listeners to add. See {@link #addListener}.
2652  * @static
2653  */
2654 Ext.util.Observable.observeClass = function(c, listeners){
2655     if(c){
2656       if(!c.fireEvent){
2657           Ext.apply(c, new Ext.util.Observable());
2658           Ext.util.Observable.capture(c.prototype, c.fireEvent, c);
2659       }
2660       if(typeof listeners == 'object'){
2661           c.on(listeners);
2662       }
2663       return c;
2664    }
2665 };
2666 /**
2667  * @class Ext.EventManager
2668  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides
2669  * several useful events directly.
2670  * See {@link Ext.EventObject} for more details on normalized event objects.
2671  * @singleton
2672  */
2673
2674 Ext.EventManager = function(){
2675     var docReadyEvent,
2676         docReadyProcId,
2677         docReadyState = false,
2678         DETECT_NATIVE = Ext.isGecko || Ext.isWebKit || Ext.isSafari,
2679         E = Ext.lib.Event,
2680         D = Ext.lib.Dom,
2681         DOC = document,
2682         WINDOW = window,
2683         DOMCONTENTLOADED = "DOMContentLoaded",
2684         COMPLETE = 'complete',
2685         propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/,
2686         /*
2687          * This cache is used to hold special js objects, the document and window, that don't have an id. We need to keep
2688          * a reference to them so we can look them up at a later point.
2689          */
2690         specialElCache = [];
2691
2692      function getId(el){
2693         var id = false,
2694             i = 0,
2695             len = specialElCache.length,
2696             id = false,
2697             skip = false,
2698             o;
2699         if(el){
2700             if(el.getElementById || el.navigator){
2701                 // look up the id
2702                 for(; i < len; ++i){
2703                     o = specialElCache[i];
2704                     if(o.el === el){
2705                         id = o.id;
2706                         break;
2707                     }
2708                 }
2709                 if(!id){
2710                     // for browsers that support it, ensure that give the el the same id
2711                     id = Ext.id(el);
2712                     specialElCache.push({
2713                         id: id,
2714                         el: el
2715                     });
2716                     skip = true;
2717                 }
2718             }else{
2719                 id = Ext.id(el);
2720             }
2721             if(!Ext.elCache[id]){
2722                 Ext.Element.addToCache(new Ext.Element(el), id);
2723                 if(skip){
2724                     Ext.elCache[id].skipGC = true;
2725                 }
2726             }
2727         }
2728         return id;
2729      };
2730
2731     /// There is some jquery work around stuff here that isn't needed in Ext Core.
2732     function addListener(el, ename, fn, task, wrap, scope){
2733         el = Ext.getDom(el);
2734         var id = getId(el),
2735             es = Ext.elCache[id].events,
2736             wfn;
2737
2738         wfn = E.on(el, ename, wrap);
2739         es[ename] = es[ename] || [];
2740
2741         /* 0 = Original Function,
2742            1 = Event Manager Wrapped Function,
2743            2 = Scope,
2744            3 = Adapter Wrapped Function,
2745            4 = Buffered Task
2746         */
2747         es[ename].push([fn, wrap, scope, wfn, task]);
2748
2749         // this is a workaround for jQuery and should somehow be removed from Ext Core in the future
2750         // without breaking ExtJS.
2751
2752         // workaround for jQuery
2753         if(el.addEventListener && ename == "mousewheel"){
2754             var args = ["DOMMouseScroll", wrap, false];
2755             el.addEventListener.apply(el, args);
2756             Ext.EventManager.addListener(WINDOW, 'unload', function(){
2757                 el.removeEventListener.apply(el, args);
2758             });
2759         }
2760
2761         // fix stopped mousedowns on the document
2762         if(el == DOC && ename == "mousedown"){
2763             Ext.EventManager.stoppedMouseDownEvent.addListener(wrap);
2764         }
2765     };
2766
2767     function doScrollChk(){
2768         /* Notes:
2769              'doScroll' will NOT work in a IFRAME/FRAMESET.
2770              The method succeeds but, a DOM query done immediately after -- FAILS.
2771           */
2772         if(window != top){
2773             return false;
2774         }
2775
2776         try{
2777             DOC.documentElement.doScroll('left');
2778         }catch(e){
2779              return false;
2780         }
2781
2782         fireDocReady();
2783         return true;
2784     }
2785     /**
2786      * @return {Boolean} True if the document is in a 'complete' state (or was determined to
2787      * be true by other means). If false, the state is evaluated again until canceled.
2788      */
2789     function checkReadyState(e){
2790
2791         if(Ext.isIE && doScrollChk()){
2792             return true;
2793         }
2794         if(DOC.readyState == COMPLETE){
2795             fireDocReady();
2796             return true;
2797         }
2798         docReadyState || (docReadyProcId = setTimeout(arguments.callee, 2));
2799         return false;
2800     }
2801
2802     var styles;
2803     function checkStyleSheets(e){
2804         styles || (styles = Ext.query('style, link[rel=stylesheet]'));
2805         if(styles.length == DOC.styleSheets.length){
2806             fireDocReady();
2807             return true;
2808         }
2809         docReadyState || (docReadyProcId = setTimeout(arguments.callee, 2));
2810         return false;
2811     }
2812
2813     function OperaDOMContentLoaded(e){
2814         DOC.removeEventListener(DOMCONTENTLOADED, arguments.callee, false);
2815         checkStyleSheets();
2816     }
2817
2818     function fireDocReady(e){
2819         if(!docReadyState){
2820             docReadyState = true; //only attempt listener removal once
2821
2822             if(docReadyProcId){
2823                 clearTimeout(docReadyProcId);
2824             }
2825             if(DETECT_NATIVE) {
2826                 DOC.removeEventListener(DOMCONTENTLOADED, fireDocReady, false);
2827             }
2828             if(Ext.isIE && checkReadyState.bindIE){  //was this was actually set ??
2829                 DOC.detachEvent('onreadystatechange', checkReadyState);
2830             }
2831             E.un(WINDOW, "load", arguments.callee);
2832         }
2833         if(docReadyEvent && !Ext.isReady){
2834             Ext.isReady = true;
2835             docReadyEvent.fire();
2836             docReadyEvent.listeners = [];
2837         }
2838
2839     };
2840
2841     function initDocReady(){
2842         docReadyEvent || (docReadyEvent = new Ext.util.Event());
2843         if (DETECT_NATIVE) {
2844             DOC.addEventListener(DOMCONTENTLOADED, fireDocReady, false);
2845         }
2846         /*
2847          * Handle additional (exceptional) detection strategies here
2848          */
2849         if (Ext.isIE){
2850             //Use readystatechange as a backup AND primary detection mechanism for a FRAME/IFRAME
2851             //See if page is already loaded
2852             if(!checkReadyState()){
2853                 checkReadyState.bindIE = true;
2854                 DOC.attachEvent('onreadystatechange', checkReadyState);
2855             }
2856
2857         }else if(Ext.isOpera ){
2858             /* Notes:
2859                Opera needs special treatment needed here because CSS rules are NOT QUITE
2860                available after DOMContentLoaded is raised.
2861             */
2862
2863             //See if page is already loaded and all styleSheets are in place
2864             (DOC.readyState == COMPLETE && checkStyleSheets()) ||
2865                 DOC.addEventListener(DOMCONTENTLOADED, OperaDOMContentLoaded, false);
2866
2867         }else if (Ext.isWebKit){
2868             //Fallback for older Webkits without DOMCONTENTLOADED support
2869             checkReadyState();
2870         }
2871         // no matter what, make sure it fires on load
2872         E.on(WINDOW, "load", fireDocReady);
2873     };
2874
2875     function createTargeted(h, o){
2876         return function(){
2877             var args = Ext.toArray(arguments);
2878             if(o.target == Ext.EventObject.setEvent(args[0]).target){
2879                 h.apply(this, args);
2880             }
2881         };
2882     };
2883
2884     function createBuffered(h, o, task){
2885         return function(e){
2886             // create new event object impl so new events don't wipe out properties
2887             task.delay(o.buffer, h, null, [new Ext.EventObjectImpl(e)]);
2888         };
2889     };
2890
2891     function createSingle(h, el, ename, fn, scope){
2892         return function(e){
2893             Ext.EventManager.removeListener(el, ename, fn, scope);
2894             h(e);
2895         };
2896     };
2897
2898     function createDelayed(h, o, fn){
2899         return function(e){
2900             var task = new Ext.util.DelayedTask(h);
2901             if(!fn.tasks) {
2902                 fn.tasks = [];
2903             }
2904             fn.tasks.push(task);
2905             task.delay(o.delay || 10, h, null, [new Ext.EventObjectImpl(e)]);
2906         };
2907     };
2908
2909     function listen(element, ename, opt, fn, scope){
2910         var o = (!opt || typeof opt == "boolean") ? {} : opt,
2911             el = Ext.getDom(element), task;
2912
2913         fn = fn || o.fn;
2914         scope = scope || o.scope;
2915
2916         if(!el){
2917             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
2918         }
2919         function h(e){
2920             // prevent errors while unload occurring
2921             if(!Ext){// !window[xname]){  ==> can't we do this?
2922                 return;
2923             }
2924             e = Ext.EventObject.setEvent(e);
2925             var t;
2926             if (o.delegate) {
2927                 if(!(t = e.getTarget(o.delegate, el))){
2928                     return;
2929                 }
2930             } else {
2931                 t = e.target;
2932             }
2933             if (o.stopEvent) {
2934                 e.stopEvent();
2935             }
2936             if (o.preventDefault) {
2937                e.preventDefault();
2938             }
2939             if (o.stopPropagation) {
2940                 e.stopPropagation();
2941             }
2942             if (o.normalized) {
2943                 e = e.browserEvent;
2944             }
2945
2946             fn.call(scope || el, e, t, o);
2947         };
2948         if(o.target){
2949             h = createTargeted(h, o);
2950         }
2951         if(o.delay){
2952             h = createDelayed(h, o, fn);
2953         }
2954         if(o.single){
2955             h = createSingle(h, el, ename, fn, scope);
2956         }
2957         if(o.buffer){
2958             task = new Ext.util.DelayedTask(h);
2959             h = createBuffered(h, o, task);
2960         }
2961
2962         addListener(el, ename, fn, task, h, scope);
2963         return h;
2964     };
2965
2966     var pub = {
2967         /**
2968          * Appends an event handler to an element.  The shorthand version {@link #on} is equivalent.  Typically you will
2969          * use {@link Ext.Element#addListener} directly on an Element in favor of calling this version.
2970          * @param {String/HTMLElement} el The html element or id to assign the event handler to.
2971          * @param {String} eventName The name of the event to listen for.
2972          * @param {Function} handler The handler function the event invokes. This function is passed
2973          * the following parameters:<ul>
2974          * <li>evt : EventObject<div class="sub-desc">The {@link Ext.EventObject EventObject} describing the event.</div></li>
2975          * <li>t : Element<div class="sub-desc">The {@link Ext.Element Element} which was the target of the event.
2976          * Note that this may be filtered by using the <tt>delegate</tt> option.</div></li>
2977          * <li>o : Object<div class="sub-desc">The options object from the addListener call.</div></li>
2978          * </ul>
2979          * @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>.
2980          * @param {Object} options (optional) An object containing handler configuration properties.
2981          * This may contain any of the following properties:<ul>
2982          * <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>
2983          * <li>delegate : String<div class="sub-desc">A simple selector to filter the target or look for a descendant of the target</div></li>
2984          * <li>stopEvent : Boolean<div class="sub-desc">True to stop the event. That is stop propagation, and prevent the default action.</div></li>
2985          * <li>preventDefault : Boolean<div class="sub-desc">True to prevent the default action</div></li>
2986          * <li>stopPropagation : Boolean<div class="sub-desc">True to prevent event propagation</div></li>
2987          * <li>normalized : Boolean<div class="sub-desc">False to pass a browser event to the handler function instead of an Ext.EventObject</div></li>
2988          * <li>delay : Number<div class="sub-desc">The number of milliseconds to delay the invocation of the handler after te event fires.</div></li>
2989          * <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>
2990          * <li>buffer : Number<div class="sub-desc">Causes the handler to be scheduled to run in an {@link Ext.util.DelayedTask} delayed
2991          * by the specified number of milliseconds. If the event fires again within that time, the original
2992          * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</div></li>
2993          * <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>
2994          * </ul><br>
2995          * <p>See {@link Ext.Element#addListener} for examples of how to use these options.</p>
2996          */
2997         addListener : function(element, eventName, fn, scope, options){
2998             if(typeof eventName == 'object'){
2999                 var o = eventName, e, val;
3000                 for(e in o){
3001                     val = o[e];
3002                     if(!propRe.test(e)){
3003                         if(Ext.isFunction(val)){
3004                             // shared options
3005                             listen(element, e, o, val, o.scope);
3006                         }else{
3007                             // individual options
3008                             listen(element, e, val);
3009                         }
3010                     }
3011                 }
3012             } else {
3013                 listen(element, eventName, options, fn, scope);
3014             }
3015         },
3016
3017         /**
3018          * Removes an event handler from an element.  The shorthand version {@link #un} is equivalent.  Typically
3019          * you will use {@link Ext.Element#removeListener} directly on an Element in favor of calling this version.
3020          * @param {String/HTMLElement} el The id or html element from which to remove the listener.
3021          * @param {String} eventName The name of the event.
3022          * @param {Function} fn The handler function to remove. <b>This must be a reference to the function passed into the {@link #addListener} call.</b>
3023          * @param {Object} scope If a scope (<b><code>this</code></b> reference) was specified when the listener was added,
3024          * then this must refer to the same object.
3025          */
3026         removeListener : function(el, eventName, fn, scope){
3027             el = Ext.getDom(el);
3028             var id = getId(el),
3029                 f = el && (Ext.elCache[id].events)[eventName] || [],
3030                 wrap, i, l, k, len, fnc;
3031
3032             for (i = 0, len = f.length; i < len; i++) {
3033
3034                 /* 0 = Original Function,
3035                    1 = Event Manager Wrapped Function,
3036                    2 = Scope,
3037                    3 = Adapter Wrapped Function,
3038                    4 = Buffered Task
3039                 */
3040                 if (Ext.isArray(fnc = f[i]) && fnc[0] == fn && (!scope || fnc[2] == scope)) {
3041                     if(fnc[4]) {
3042                         fnc[4].cancel();
3043                     }
3044                     k = fn.tasks && fn.tasks.length;
3045                     if(k) {
3046                         while(k--) {
3047                             fn.tasks[k].cancel();
3048                         }
3049                         delete fn.tasks;
3050                     }
3051                     wrap = fnc[1];
3052                     E.un(el, eventName, E.extAdapter ? fnc[3] : wrap);
3053
3054                     // jQuery workaround that should be removed from Ext Core
3055                     if(wrap && el.addEventListener && eventName == "mousewheel"){
3056                         el.removeEventListener("DOMMouseScroll", wrap, false);
3057                     }
3058
3059                     // fix stopped mousedowns on the document
3060                     if(wrap && el == DOC && eventName == "mousedown"){
3061                         Ext.EventManager.stoppedMouseDownEvent.removeListener(wrap);
3062                     }
3063
3064                     f.splice(i, 1);
3065                     if (f.length === 0) {
3066                         delete Ext.elCache[id].events[eventName];
3067                     }
3068                     for (k in Ext.elCache[id].events) {
3069                         return false;
3070                     }
3071                     Ext.elCache[id].events = {};
3072                     return false;
3073                 }
3074             }
3075         },
3076
3077         /**
3078          * Removes all event handers from an element.  Typically you will use {@link Ext.Element#removeAllListeners}
3079          * directly on an Element in favor of calling this version.
3080          * @param {String/HTMLElement} el The id or html element from which to remove all event handlers.
3081          */
3082         removeAll : function(el){
3083             el = Ext.getDom(el);
3084             var id = getId(el),
3085                 ec = Ext.elCache[id] || {},
3086                 es = ec.events || {},
3087                 f, i, len, ename, fn, k, wrap;
3088
3089             for(ename in es){
3090                 if(es.hasOwnProperty(ename)){
3091                     f = es[ename];
3092                     /* 0 = Original Function,
3093                        1 = Event Manager Wrapped Function,
3094                        2 = Scope,
3095                        3 = Adapter Wrapped Function,
3096                        4 = Buffered Task
3097                     */
3098                     for (i = 0, len = f.length; i < len; i++) {
3099                         fn = f[i];
3100                         if(fn[4]) {
3101                             fn[4].cancel();
3102                         }
3103                         if(fn[0].tasks && (k = fn[0].tasks.length)) {
3104                             while(k--) {
3105                                 fn[0].tasks[k].cancel();
3106                             }
3107                             delete fn.tasks;
3108                         }
3109                         wrap =  fn[1];
3110                         E.un(el, ename, E.extAdapter ? fn[3] : wrap);
3111
3112                         // jQuery workaround that should be removed from Ext Core
3113                         if(el.addEventListener && wrap && ename == "mousewheel"){
3114                             el.removeEventListener("DOMMouseScroll", wrap, false);
3115                         }
3116
3117                         // fix stopped mousedowns on the document
3118                         if(wrap && el == DOC &&  ename == "mousedown"){
3119                             Ext.EventManager.stoppedMouseDownEvent.removeListener(wrap);
3120                         }
3121                     }
3122                 }
3123             }
3124             if (Ext.elCache[id]) {
3125                 Ext.elCache[id].events = {};
3126             }
3127         },
3128
3129         getListeners : function(el, eventName) {
3130             el = Ext.getDom(el);
3131             var id = getId(el),
3132                 ec = Ext.elCache[id] || {},
3133                 es = ec.events || {},
3134                 results = [];
3135             if (es && es[eventName]) {
3136                 return es[eventName];
3137             } else {
3138                 return null;
3139             }
3140         },
3141
3142         purgeElement : function(el, recurse, eventName) {
3143             el = Ext.getDom(el);
3144             var id = getId(el),
3145                 ec = Ext.elCache[id] || {},
3146                 es = ec.events || {},
3147                 i, f, len;
3148             if (eventName) {
3149                 if (es && es.hasOwnProperty(eventName)) {
3150                     f = es[eventName];
3151                     for (i = 0, len = f.length; i < len; i++) {
3152                         Ext.EventManager.removeListener(el, eventName, f[i][0]);
3153                     }
3154                 }
3155             } else {
3156                 Ext.EventManager.removeAll(el);
3157             }
3158             if (recurse && el && el.childNodes) {
3159                 for (i = 0, len = el.childNodes.length; i < len; i++) {
3160                     Ext.EventManager.purgeElement(el.childNodes[i], recurse, eventName);
3161                 }
3162             }
3163         },
3164
3165         _unload : function() {
3166             var el;
3167             for (el in Ext.elCache) {
3168                 Ext.EventManager.removeAll(el);
3169             }
3170             delete Ext.elCache;
3171             delete Ext.Element._flyweights;
3172
3173             // Abort any outstanding Ajax requests
3174             var c,
3175                 conn,
3176                 tid,
3177                 ajax = Ext.lib.Ajax;
3178             (typeof ajax.conn == 'object') ? conn = ajax.conn : conn = {};
3179             for (tid in conn) {
3180                 c = conn[tid];
3181                 if (c) {
3182                     ajax.abort({conn: c, tId: tid});
3183                 }
3184             }
3185         },
3186         /**
3187          * Adds a listener to be notified when the document is ready (before onload and before images are loaded). Can be
3188          * accessed shorthanded as Ext.onReady().
3189          * @param {Function} fn The method the event invokes.
3190          * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the handler function executes. Defaults to the browser window.
3191          * @param {boolean} options (optional) Options object as passed to {@link Ext.Element#addListener}. It is recommended that the options
3192          * <code>{single: true}</code> be used so that the handler is removed on first invocation.
3193          */
3194         onDocumentReady : function(fn, scope, options){
3195             if(Ext.isReady){ // if it already fired or document.body is present
3196                 docReadyEvent || (docReadyEvent = new Ext.util.Event());
3197                 docReadyEvent.addListener(fn, scope, options);
3198                 docReadyEvent.fire();
3199                 docReadyEvent.listeners = [];
3200             }else{
3201                 if(!docReadyEvent){
3202                     initDocReady();
3203                 }
3204                 options = options || {};
3205                 options.delay = options.delay || 1;
3206                 docReadyEvent.addListener(fn, scope, options);
3207             }
3208         },
3209
3210         /**
3211          * Forces a document ready state transition for the framework.  Used when Ext is loaded
3212          * into a DOM structure AFTER initial page load (Google API or other dynamic load scenario.
3213          * Any pending 'onDocumentReady' handlers will be fired (if not already handled).
3214          */
3215         fireDocReady  : fireDocReady
3216     };
3217      /**
3218      * Appends an event handler to an element.  Shorthand for {@link #addListener}.
3219      * @param {String/HTMLElement} el The html element or id to assign the event handler to
3220      * @param {String} eventName The name of the event to listen for.
3221      * @param {Function} handler The handler function the event invokes.
3222      * @param {Object} scope (optional) (<code>this</code> reference) in which the handler function executes. <b>Defaults to the Element</b>.
3223      * @param {Object} options (optional) An object containing standard {@link #addListener} options
3224      * @member Ext.EventManager
3225      * @method on
3226      */
3227     pub.on = pub.addListener;
3228     /**
3229      * Removes an event handler from an element.  Shorthand for {@link #removeListener}.
3230      * @param {String/HTMLElement} el The id or html element from which to remove the listener.
3231      * @param {String} eventName The name of the event.
3232      * @param {Function} fn The handler function to remove. <b>This must be a reference to the function passed into the {@link #on} call.</b>
3233      * @param {Object} scope If a scope (<b><code>this</code></b> reference) was specified when the listener was added,
3234      * then this must refer to the same object.
3235      * @member Ext.EventManager
3236      * @method un
3237      */
3238     pub.un = pub.removeListener;
3239
3240     pub.stoppedMouseDownEvent = new Ext.util.Event();
3241     return pub;
3242 }();
3243 /**
3244   * Adds a listener to be notified when the document is ready (before onload and before images are loaded). Shorthand of {@link Ext.EventManager#onDocumentReady}.
3245   * @param {Function} fn The method the event invokes.
3246   * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the handler function executes. Defaults to the browser window.
3247   * @param {boolean} options (optional) Options object as passed to {@link Ext.Element#addListener}. It is recommended that the options
3248   * <code>{single: true}</code> be used so that the handler is removed on first invocation.
3249   * @member Ext
3250   * @method onReady
3251  */
3252 Ext.onReady = Ext.EventManager.onDocumentReady;
3253
3254
3255 //Initialize doc classes
3256 (function(){
3257
3258     var initExtCss = function(){
3259         // find the body element
3260         var bd = document.body || document.getElementsByTagName('body')[0];
3261         if(!bd){ return false; }
3262         var cls = [' ',
3263                 Ext.isIE ? "ext-ie " + (Ext.isIE6 ? 'ext-ie6' : (Ext.isIE7 ? 'ext-ie7' : 'ext-ie8'))
3264                 : Ext.isGecko ? "ext-gecko " + (Ext.isGecko2 ? 'ext-gecko2' : 'ext-gecko3')
3265                 : Ext.isOpera ? "ext-opera"
3266                 : Ext.isWebKit ? "ext-webkit" : ""];
3267
3268         if(Ext.isSafari){
3269             cls.push("ext-safari " + (Ext.isSafari2 ? 'ext-safari2' : (Ext.isSafari3 ? 'ext-safari3' : 'ext-safari4')));
3270         }else if(Ext.isChrome){
3271             cls.push("ext-chrome");
3272         }
3273
3274         if(Ext.isMac){
3275             cls.push("ext-mac");
3276         }
3277         if(Ext.isLinux){
3278             cls.push("ext-linux");
3279         }
3280
3281         if(Ext.isStrict || Ext.isBorderBox){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
3282             var p = bd.parentNode;
3283             if(p){
3284                 p.className += Ext.isStrict ? ' ext-strict' : ' ext-border-box';
3285             }
3286         }
3287         bd.className += cls.join(' ');
3288         return true;
3289     }
3290
3291     if(!initExtCss()){
3292         Ext.onReady(initExtCss);
3293     }
3294 })();
3295
3296
3297 /**
3298  * @class Ext.EventObject
3299  * Just as {@link Ext.Element} wraps around a native DOM node, Ext.EventObject
3300  * wraps the browser's native event-object normalizing cross-browser differences,
3301  * such as which mouse button is clicked, keys pressed, mechanisms to stop
3302  * event-propagation along with a method to prevent default actions from taking place.
3303  * <p>For example:</p>
3304  * <pre><code>
3305 function handleClick(e, t){ // e is not a standard event object, it is a Ext.EventObject
3306     e.preventDefault();
3307     var target = e.getTarget(); // same as t (the target HTMLElement)
3308     ...
3309 }
3310 var myDiv = {@link Ext#get Ext.get}("myDiv");  // get reference to an {@link Ext.Element}
3311 myDiv.on(         // 'on' is shorthand for addListener
3312     "click",      // perform an action on click of myDiv
3313     handleClick   // reference to the action handler
3314 );
3315 // other methods to do the same:
3316 Ext.EventManager.on("myDiv", 'click', handleClick);
3317 Ext.EventManager.addListener("myDiv", 'click', handleClick);
3318  </code></pre>
3319  * @singleton
3320  */
3321 Ext.EventObject = function(){
3322     var E = Ext.lib.Event,
3323         clickRe = /(dbl)?click/,
3324         // safari keypress events for special keys return bad keycodes
3325         safariKeys = {
3326             3 : 13, // enter
3327             63234 : 37, // left
3328             63235 : 39, // right
3329             63232 : 38, // up
3330             63233 : 40, // down
3331             63276 : 33, // page up
3332             63277 : 34, // page down
3333             63272 : 46, // delete
3334             63273 : 36, // home
3335             63275 : 35  // end
3336         },
3337         // normalize button clicks
3338         btnMap = Ext.isIE ? {1:0,4:1,2:2} :
3339                 (Ext.isWebKit ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
3340
3341     Ext.EventObjectImpl = function(e){
3342         if(e){
3343             this.setEvent(e.browserEvent || e);
3344         }
3345     };
3346
3347     Ext.EventObjectImpl.prototype = {
3348            /** @private */
3349         setEvent : function(e){
3350             var me = this;
3351             if(e == me || (e && e.browserEvent)){ // already wrapped
3352                 return e;
3353             }
3354             me.browserEvent = e;
3355             if(e){
3356                 // normalize buttons
3357                 me.button = e.button ? btnMap[e.button] : (e.which ? e.which - 1 : -1);
3358                 if(clickRe.test(e.type) && me.button == -1){
3359                     me.button = 0;
3360                 }
3361                 me.type = e.type;
3362                 me.shiftKey = e.shiftKey;
3363                 // mac metaKey behaves like ctrlKey
3364                 me.ctrlKey = e.ctrlKey || e.metaKey || false;
3365                 me.altKey = e.altKey;
3366                 // in getKey these will be normalized for the mac
3367                 me.keyCode = e.keyCode;
3368                 me.charCode = e.charCode;
3369                 // cache the target for the delayed and or buffered events
3370                 me.target = E.getTarget(e);
3371                 // same for XY
3372                 me.xy = E.getXY(e);
3373             }else{
3374                 me.button = -1;
3375                 me.shiftKey = false;
3376                 me.ctrlKey = false;
3377                 me.altKey = false;
3378                 me.keyCode = 0;
3379                 me.charCode = 0;
3380                 me.target = null;
3381                 me.xy = [0, 0];
3382             }
3383             return me;
3384         },
3385
3386         /**
3387          * Stop the event (preventDefault and stopPropagation)
3388          */
3389         stopEvent : function(){
3390             var me = this;
3391             if(me.browserEvent){
3392                 if(me.browserEvent.type == 'mousedown'){
3393                     Ext.EventManager.stoppedMouseDownEvent.fire(me);
3394                 }
3395                 E.stopEvent(me.browserEvent);
3396             }
3397         },
3398
3399         /**
3400          * Prevents the browsers default handling of the event.
3401          */
3402         preventDefault : function(){
3403             if(this.browserEvent){
3404                 E.preventDefault(this.browserEvent);
3405             }
3406         },
3407
3408         /**
3409          * Cancels bubbling of the event.
3410          */
3411         stopPropagation : function(){
3412             var me = this;
3413             if(me.browserEvent){
3414                 if(me.browserEvent.type == 'mousedown'){
3415                     Ext.EventManager.stoppedMouseDownEvent.fire(me);
3416                 }
3417                 E.stopPropagation(me.browserEvent);
3418             }
3419         },
3420
3421         /**
3422          * Gets the character code for the event.
3423          * @return {Number}
3424          */
3425         getCharCode : function(){
3426             return this.charCode || this.keyCode;
3427         },
3428
3429         /**
3430          * Returns a normalized keyCode for the event.
3431          * @return {Number} The key code
3432          */
3433         getKey : function(){
3434             return this.normalizeKey(this.keyCode || this.charCode)
3435         },
3436
3437         // private
3438         normalizeKey: function(k){
3439             return Ext.isSafari ? (safariKeys[k] || k) : k;
3440         },
3441
3442         /**
3443          * Gets the x coordinate of the event.
3444          * @return {Number}
3445          */
3446         getPageX : function(){
3447             return this.xy[0];
3448         },
3449
3450         /**
3451          * Gets the y coordinate of the event.
3452          * @return {Number}
3453          */
3454         getPageY : function(){
3455             return this.xy[1];
3456         },
3457
3458         /**
3459          * Gets the page coordinates of the event.
3460          * @return {Array} The xy values like [x, y]
3461          */
3462         getXY : function(){
3463             return this.xy;
3464         },
3465
3466         /**
3467          * Gets the target for the event.
3468          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
3469          * @param {Number/Mixed} maxDepth (optional) The max depth to
3470                 search as a number or element (defaults to 10 || document.body)
3471          * @param {Boolean} returnEl (optional) True to return a Ext.Element object instead of DOM node
3472          * @return {HTMLelement}
3473          */
3474         getTarget : function(selector, maxDepth, returnEl){
3475             return selector ? Ext.fly(this.target).findParent(selector, maxDepth, returnEl) : (returnEl ? Ext.get(this.target) : this.target);
3476         },
3477
3478         /**
3479          * Gets the related target.
3480          * @return {HTMLElement}
3481          */
3482         getRelatedTarget : function(){
3483             return this.browserEvent ? E.getRelatedTarget(this.browserEvent) : null;
3484         },
3485
3486         /**
3487          * Normalizes mouse wheel delta across browsers
3488          * @return {Number} The delta
3489          */
3490         getWheelDelta : function(){
3491             var e = this.browserEvent;
3492             var delta = 0;
3493             if(e.wheelDelta){ /* IE/Opera. */
3494                 delta = e.wheelDelta/120;
3495             }else if(e.detail){ /* Mozilla case. */
3496                 delta = -e.detail/3;
3497             }
3498             return delta;
3499         },
3500
3501         /**
3502         * 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.
3503         * Example usage:<pre><code>
3504         // Handle click on any child of an element
3505         Ext.getBody().on('click', function(e){
3506             if(e.within('some-el')){
3507                 alert('Clicked on a child of some-el!');
3508             }
3509         });
3510
3511         // Handle click directly on an element, ignoring clicks on child nodes
3512         Ext.getBody().on('click', function(e,t){
3513             if((t.id == 'some-el') && !e.within(t, true)){
3514                 alert('Clicked directly on some-el!');
3515             }
3516         });
3517         </code></pre>
3518          * @param {Mixed} el The id, DOM element or Ext.Element to check
3519          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
3520          * @param {Boolean} allowEl {optional} true to also check if the passed element is the target or related target
3521          * @return {Boolean}
3522          */
3523         within : function(el, related, allowEl){
3524             if(el){
3525                 var t = this[related ? "getRelatedTarget" : "getTarget"]();
3526                 return t && ((allowEl ? (t == Ext.getDom(el)) : false) || Ext.fly(el).contains(t));
3527             }
3528             return false;
3529         }
3530      };
3531
3532     return new Ext.EventObjectImpl();
3533 }();
3534 /**
3535 * @class Ext.EventManager
3536 */
3537 Ext.apply(Ext.EventManager, function(){
3538    var resizeEvent,
3539        resizeTask,
3540        textEvent,
3541        textSize,
3542        D = Ext.lib.Dom,
3543        propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/,
3544        curWidth = 0,
3545        curHeight = 0,
3546        // note 1: IE fires ONLY the keydown event on specialkey autorepeat
3547        // note 2: Safari < 3.1, Gecko (Mac/Linux) & Opera fire only the keypress event on specialkey autorepeat
3548        // (research done by @Jan Wolter at http://unixpapa.com/js/key.html)
3549        useKeydown = Ext.isWebKit ?
3550                    Ext.num(navigator.userAgent.match(/AppleWebKit\/(\d+)/)[1]) >= 525 :
3551                    !((Ext.isGecko && !Ext.isWindows) || Ext.isOpera);
3552
3553    return {
3554        // private
3555        doResizeEvent: function(){
3556            var h = D.getViewHeight(),
3557                w = D.getViewWidth();
3558
3559             //whacky problem in IE where the resize event will fire even though the w/h are the same.
3560             if(curHeight != h || curWidth != w){
3561                resizeEvent.fire(curWidth = w, curHeight = h);
3562             }
3563        },
3564
3565        /**
3566         * Adds a listener to be notified when the browser window is resized and provides resize event buffering (100 milliseconds),
3567         * passes new viewport width and height to handlers.
3568         * @param {Function} fn      The handler function the window resize event invokes.
3569         * @param {Object}   scope   The scope (<code>this</code> reference) in which the handler function executes. Defaults to the browser window.
3570         * @param {boolean}  options Options object as passed to {@link Ext.Element#addListener}
3571         */
3572        onWindowResize : function(fn, scope, options){
3573            if(!resizeEvent){
3574                resizeEvent = new Ext.util.Event();
3575                resizeTask = new Ext.util.DelayedTask(this.doResizeEvent);
3576                Ext.EventManager.on(window, "resize", this.fireWindowResize, this);
3577            }
3578            resizeEvent.addListener(fn, scope, options);
3579        },
3580
3581        // exposed only to allow manual firing
3582        fireWindowResize : function(){
3583            if(resizeEvent){
3584                resizeTask.delay(100);
3585            }
3586        },
3587
3588        /**
3589         * 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.
3590         * @param {Function} fn      The function the event invokes.
3591         * @param {Object}   scope   The scope (<code>this</code> reference) in which the handler function executes. Defaults to the browser window.
3592         * @param {boolean}  options Options object as passed to {@link Ext.Element#addListener}
3593         */
3594        onTextResize : function(fn, scope, options){
3595            if(!textEvent){
3596                textEvent = new Ext.util.Event();
3597                var textEl = new Ext.Element(document.createElement('div'));
3598                textEl.dom.className = 'x-text-resize';
3599                textEl.dom.innerHTML = 'X';
3600                textEl.appendTo(document.body);
3601                textSize = textEl.dom.offsetHeight;
3602                setInterval(function(){
3603                    if(textEl.dom.offsetHeight != textSize){
3604                        textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
3605                    }
3606                }, this.textResizeInterval);
3607            }
3608            textEvent.addListener(fn, scope, options);
3609        },
3610
3611        /**
3612         * Removes the passed window resize listener.
3613         * @param {Function} fn        The method the event invokes
3614         * @param {Object}   scope    The scope of handler
3615         */
3616        removeResizeListener : function(fn, scope){
3617            if(resizeEvent){
3618                resizeEvent.removeListener(fn, scope);
3619            }
3620        },
3621
3622        // private
3623        fireResize : function(){
3624            if(resizeEvent){
3625                resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
3626            }
3627        },
3628
3629         /**
3630         * The frequency, in milliseconds, to check for text resize events (defaults to 50)
3631         */
3632        textResizeInterval : 50,
3633
3634        /**
3635         * Url used for onDocumentReady with using SSL (defaults to Ext.SSL_SECURE_URL)
3636         */
3637        ieDeferSrc : false,
3638
3639        // protected for use inside the framework
3640        // detects whether we should use keydown or keypress based on the browser.
3641        useKeydown: useKeydown
3642    };
3643 }());
3644
3645 Ext.EventManager.on = Ext.EventManager.addListener;
3646
3647
3648 Ext.apply(Ext.EventObjectImpl.prototype, {
3649    /** Key constant @type Number */
3650    BACKSPACE: 8,
3651    /** Key constant @type Number */
3652    TAB: 9,
3653    /** Key constant @type Number */
3654    NUM_CENTER: 12,
3655    /** Key constant @type Number */
3656    ENTER: 13,
3657    /** Key constant @type Number */
3658    RETURN: 13,
3659    /** Key constant @type Number */
3660    SHIFT: 16,
3661    /** Key constant @type Number */
3662    CTRL: 17,
3663    CONTROL : 17, // legacy
3664    /** Key constant @type Number */
3665    ALT: 18,
3666    /** Key constant @type Number */
3667    PAUSE: 19,
3668    /** Key constant @type Number */
3669    CAPS_LOCK: 20,
3670    /** Key constant @type Number */
3671    ESC: 27,
3672    /** Key constant @type Number */
3673    SPACE: 32,
3674    /** Key constant @type Number */
3675    PAGE_UP: 33,
3676    PAGEUP : 33, // legacy
3677    /** Key constant @type Number */
3678    PAGE_DOWN: 34,
3679    PAGEDOWN : 34, // legacy
3680    /** Key constant @type Number */
3681    END: 35,
3682    /** Key constant @type Number */
3683    HOME: 36,
3684    /** Key constant @type Number */
3685    LEFT: 37,
3686    /** Key constant @type Number */
3687    UP: 38,
3688    /** Key constant @type Number */
3689    RIGHT: 39,
3690    /** Key constant @type Number */
3691    DOWN: 40,
3692    /** Key constant @type Number */
3693    PRINT_SCREEN: 44,
3694    /** Key constant @type Number */
3695    INSERT: 45,
3696    /** Key constant @type Number */
3697    DELETE: 46,
3698    /** Key constant @type Number */
3699    ZERO: 48,
3700    /** Key constant @type Number */
3701    ONE: 49,
3702    /** Key constant @type Number */
3703    TWO: 50,
3704    /** Key constant @type Number */
3705    THREE: 51,
3706    /** Key constant @type Number */
3707    FOUR: 52,
3708    /** Key constant @type Number */
3709    FIVE: 53,
3710    /** Key constant @type Number */
3711    SIX: 54,
3712    /** Key constant @type Number */
3713    SEVEN: 55,
3714    /** Key constant @type Number */
3715    EIGHT: 56,
3716    /** Key constant @type Number */
3717    NINE: 57,
3718    /** Key constant @type Number */
3719    A: 65,
3720    /** Key constant @type Number */
3721    B: 66,
3722    /** Key constant @type Number */
3723    C: 67,
3724    /** Key constant @type Number */
3725    D: 68,
3726    /** Key constant @type Number */
3727    E: 69,
3728    /** Key constant @type Number */
3729    F: 70,
3730    /** Key constant @type Number */
3731    G: 71,
3732    /** Key constant @type Number */
3733    H: 72,
3734    /** Key constant @type Number */
3735    I: 73,
3736    /** Key constant @type Number */
3737    J: 74,
3738    /** Key constant @type Number */
3739    K: 75,
3740    /** Key constant @type Number */
3741    L: 76,
3742    /** Key constant @type Number */
3743    M: 77,
3744    /** Key constant @type Number */
3745    N: 78,
3746    /** Key constant @type Number */
3747    O: 79,
3748    /** Key constant @type Number */
3749    P: 80,
3750    /** Key constant @type Number */
3751    Q: 81,
3752    /** Key constant @type Number */
3753    R: 82,
3754    /** Key constant @type Number */
3755    S: 83,
3756    /** Key constant @type Number */
3757    T: 84,
3758    /** Key constant @type Number */
3759    U: 85,
3760    /** Key constant @type Number */
3761    V: 86,
3762    /** Key constant @type Number */
3763    W: 87,
3764    /** Key constant @type Number */
3765    X: 88,
3766    /** Key constant @type Number */
3767    Y: 89,
3768    /** Key constant @type Number */
3769    Z: 90,
3770    /** Key constant @type Number */
3771    CONTEXT_MENU: 93,
3772    /** Key constant @type Number */
3773    NUM_ZERO: 96,
3774    /** Key constant @type Number */
3775    NUM_ONE: 97,
3776    /** Key constant @type Number */
3777    NUM_TWO: 98,
3778    /** Key constant @type Number */
3779    NUM_THREE: 99,
3780    /** Key constant @type Number */
3781    NUM_FOUR: 100,
3782    /** Key constant @type Number */
3783    NUM_FIVE: 101,
3784    /** Key constant @type Number */
3785    NUM_SIX: 102,
3786    /** Key constant @type Number */
3787    NUM_SEVEN: 103,
3788    /** Key constant @type Number */
3789    NUM_EIGHT: 104,
3790    /** Key constant @type Number */
3791    NUM_NINE: 105,
3792    /** Key constant @type Number */
3793    NUM_MULTIPLY: 106,
3794    /** Key constant @type Number */
3795    NUM_PLUS: 107,
3796    /** Key constant @type Number */
3797    NUM_MINUS: 109,
3798    /** Key constant @type Number */
3799    NUM_PERIOD: 110,
3800    /** Key constant @type Number */
3801    NUM_DIVISION: 111,
3802    /** Key constant @type Number */
3803    F1: 112,
3804    /** Key constant @type Number */
3805    F2: 113,
3806    /** Key constant @type Number */
3807    F3: 114,
3808    /** Key constant @type Number */
3809    F4: 115,
3810    /** Key constant @type Number */
3811    F5: 116,
3812    /** Key constant @type Number */
3813    F6: 117,
3814    /** Key constant @type Number */
3815    F7: 118,
3816    /** Key constant @type Number */
3817    F8: 119,
3818    /** Key constant @type Number */
3819    F9: 120,
3820    /** Key constant @type Number */
3821    F10: 121,
3822    /** Key constant @type Number */
3823    F11: 122,
3824    /** Key constant @type Number */
3825    F12: 123,
3826
3827    /** @private */
3828    isNavKeyPress : function(){
3829        var me = this,
3830            k = this.normalizeKey(me.keyCode);
3831        return (k >= 33 && k <= 40) ||  // Page Up/Down, End, Home, Left, Up, Right, Down
3832        k == me.RETURN ||
3833        k == me.TAB ||
3834        k == me.ESC;
3835    },
3836
3837    isSpecialKey : function(){
3838        var k = this.normalizeKey(this.keyCode);
3839        return (this.type == 'keypress' && this.ctrlKey) ||
3840        this.isNavKeyPress() ||
3841        (k == this.BACKSPACE) || // Backspace
3842        (k >= 16 && k <= 20) || // Shift, Ctrl, Alt, Pause, Caps Lock
3843        (k >= 44 && k <= 46);   // Print Screen, Insert, Delete
3844    },
3845
3846    getPoint : function(){
3847        return new Ext.lib.Point(this.xy[0], this.xy[1]);
3848    },
3849
3850    /**
3851     * Returns true if the control, meta, shift or alt key was pressed during this event.
3852     * @return {Boolean}
3853     */
3854    hasModifier : function(){
3855        return ((this.ctrlKey || this.altKey) || this.shiftKey);
3856    }
3857 });/**
3858  * @class Ext.Element
3859  * <p>Encapsulates a DOM element, adding simple DOM manipulation facilities, normalizing for browser differences.</p>
3860  * <p>All instances of this class inherit the methods of {@link Ext.Fx} making visual effects easily available to all DOM elements.</p>
3861  * <p>Note that the events documented in this class are not Ext events, they encapsulate browser events. To
3862  * access the underlying browser event, see {@link Ext.EventObject#browserEvent}. Some older
3863  * browsers may not support the full range of events. Which events are supported is beyond the control of ExtJs.</p>
3864  * Usage:<br>
3865 <pre><code>
3866 // by id
3867 var el = Ext.get("my-div");
3868
3869 // by DOM element reference
3870 var el = Ext.get(myDivElement);
3871 </code></pre>
3872  * <b>Animations</b><br />
3873  * <p>When an element is manipulated, by default there is no animation.</p>
3874  * <pre><code>
3875 var el = Ext.get("my-div");
3876
3877 // no animation
3878 el.setWidth(100);
3879  * </code></pre>
3880  * <p>Many of the functions for manipulating an element have an optional "animate" parameter.  This
3881  * parameter can be specified as boolean (<tt>true</tt>) for default animation effects.</p>
3882  * <pre><code>
3883 // default animation
3884 el.setWidth(100, true);
3885  * </code></pre>
3886  *
3887  * <p>To configure the effects, an object literal with animation options to use as the Element animation
3888  * configuration object can also be specified. Note that the supported Element animation configuration
3889  * options are a subset of the {@link Ext.Fx} animation options specific to Fx effects.  The supported
3890  * Element animation configuration options are:</p>
3891 <pre>
3892 Option    Default   Description
3893 --------- --------  ---------------------------------------------
3894 {@link Ext.Fx#duration duration}  .35       The duration of the animation in seconds
3895 {@link Ext.Fx#easing easing}    easeOut   The easing method
3896 {@link Ext.Fx#callback callback}  none      A function to execute when the anim completes
3897 {@link Ext.Fx#scope scope}     this      The scope (this) of the callback function
3898 </pre>
3899  *
3900  * <pre><code>
3901 // Element animation options object
3902 var opt = {
3903     {@link Ext.Fx#duration duration}: 1,
3904     {@link Ext.Fx#easing easing}: 'elasticIn',
3905     {@link Ext.Fx#callback callback}: this.foo,
3906     {@link Ext.Fx#scope scope}: this
3907 };
3908 // animation with some options set
3909 el.setWidth(100, opt);
3910  * </code></pre>
3911  * <p>The Element animation object being used for the animation will be set on the options
3912  * object as "anim", which allows you to stop or manipulate the animation. Here is an example:</p>
3913  * <pre><code>
3914 // using the "anim" property to get the Anim object
3915 if(opt.anim.isAnimated()){
3916     opt.anim.stop();
3917 }
3918  * </code></pre>
3919  * <p>Also see the <tt>{@link #animate}</tt> method for another animation technique.</p>
3920  * <p><b> Composite (Collections of) Elements</b></p>
3921  * <p>For working with collections of Elements, see {@link Ext.CompositeElement}</p>
3922  * @constructor Create a new Element directly.
3923  * @param {String/HTMLElement} element
3924  * @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).
3925  */
3926 (function(){
3927 var DOC = document;
3928
3929 Ext.Element = function(element, forceNew){
3930     var dom = typeof element == "string" ?
3931               DOC.getElementById(element) : element,
3932         id;
3933
3934     if(!dom) return null;
3935
3936     id = dom.id;
3937
3938     if(!forceNew && id && Ext.elCache[id]){ // element object already exists
3939         return Ext.elCache[id].el;
3940     }
3941
3942     /**
3943      * The DOM element
3944      * @type HTMLElement
3945      */
3946     this.dom = dom;
3947
3948     /**
3949      * The DOM element ID
3950      * @type String
3951      */
3952     this.id = id || Ext.id(dom);
3953 };
3954
3955 var DH = Ext.DomHelper,
3956     El = Ext.Element,
3957     EC = Ext.elCache;
3958
3959 El.prototype = {
3960     /**
3961      * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
3962      * @param {Object} o The object with the attributes
3963      * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
3964      * @return {Ext.Element} this
3965      */
3966     set : function(o, useSet){
3967         var el = this.dom,
3968             attr,
3969             val,
3970             useSet = (useSet !== false) && !!el.setAttribute;
3971
3972         for (attr in o) {
3973             if (o.hasOwnProperty(attr)) {
3974                 val = o[attr];
3975                 if (attr == 'style') {
3976                     DH.applyStyles(el, val);
3977                 } else if (attr == 'cls') {
3978                     el.className = val;
3979                 } else if (useSet) {
3980                     el.setAttribute(attr, val);
3981                 } else {
3982                     el[attr] = val;
3983                 }
3984             }
3985         }
3986         return this;
3987     },
3988
3989 //  Mouse events
3990     /**
3991      * @event click
3992      * Fires when a mouse click 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 contextmenu
3999      * Fires when a right click 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 dblclick
4006      * Fires when a mouse double click is detected within 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 mousedown
4013      * Fires when a mousedown is detected within 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 mouseup
4020      * Fires when a mouseup is detected within 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 mouseover
4027      * Fires when a mouseover is detected within 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      * @event mousemove
4034      * Fires when a mousemove is detected with the element.
4035      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4036      * @param {HtmlElement} t The target of the event.
4037      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4038      */
4039     /**
4040      * @event mouseout
4041      * Fires when a mouseout is detected with the element.
4042      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4043      * @param {HtmlElement} t The target of the event.
4044      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4045      */
4046     /**
4047      * @event mouseenter
4048      * Fires when the mouse enters the element.
4049      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4050      * @param {HtmlElement} t The target of the event.
4051      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4052      */
4053     /**
4054      * @event mouseleave
4055      * Fires when the mouse leaves the element.
4056      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4057      * @param {HtmlElement} t The target of the event.
4058      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4059      */
4060
4061 //  Keyboard events
4062     /**
4063      * @event keypress
4064      * Fires when a keypress is detected within the element.
4065      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4066      * @param {HtmlElement} t The target of the event.
4067      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4068      */
4069     /**
4070      * @event keydown
4071      * Fires when a keydown is detected within the element.
4072      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4073      * @param {HtmlElement} t The target of the event.
4074      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4075      */
4076     /**
4077      * @event keyup
4078      * Fires when a keyup is detected within the element.
4079      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4080      * @param {HtmlElement} t The target of the event.
4081      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4082      */
4083
4084
4085 //  HTML frame/object events
4086     /**
4087      * @event load
4088      * Fires when the user agent finishes loading all content within the element. Only supported by window, frames, objects and images.
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 unload
4095      * 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.
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      * @event abort
4102      * Fires when an object/image is stopped from loading before completely loaded.
4103      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4104      * @param {HtmlElement} t The target of the event.
4105      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4106      */
4107     /**
4108      * @event error
4109      * Fires when an object/image/frame cannot be loaded properly.
4110      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4111      * @param {HtmlElement} t The target of the event.
4112      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4113      */
4114     /**
4115      * @event resize
4116      * Fires when a document view is resized.
4117      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4118      * @param {HtmlElement} t The target of the event.
4119      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4120      */
4121     /**
4122      * @event scroll
4123      * Fires when a document view is scrolled.
4124      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4125      * @param {HtmlElement} t The target of the event.
4126      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4127      */
4128
4129 //  Form events
4130     /**
4131      * @event select
4132      * Fires when a user selects some text in a text field, including input and textarea.
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 change
4139      * Fires when a control loses the input focus and its value has been modified since gaining focus.
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      * @event submit
4146      * Fires when a form is submitted.
4147      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4148      * @param {HtmlElement} t The target of the event.
4149      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4150      */
4151     /**
4152      * @event reset
4153      * Fires when a form is reset.
4154      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4155      * @param {HtmlElement} t The target of the event.
4156      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4157      */
4158     /**
4159      * @event focus
4160      * Fires when an element receives focus either via the pointing device or by tab navigation.
4161      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4162      * @param {HtmlElement} t The target of the event.
4163      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4164      */
4165     /**
4166      * @event blur
4167      * Fires when an element loses focus either via the pointing device or by tabbing navigation.
4168      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4169      * @param {HtmlElement} t The target of the event.
4170      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4171      */
4172
4173 //  User Interface events
4174     /**
4175      * @event DOMFocusIn
4176      * Where supported. Similar to HTML focus event, but can be applied to any focusable element.
4177      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4178      * @param {HtmlElement} t The target of the event.
4179      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4180      */
4181     /**
4182      * @event DOMFocusOut
4183      * Where supported. Similar to HTML blur event, but can be applied to any focusable element.
4184      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4185      * @param {HtmlElement} t The target of the event.
4186      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4187      */
4188     /**
4189      * @event DOMActivate
4190      * Where supported. Fires when an element is activated, for instance, through a mouse click or a keypress.
4191      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4192      * @param {HtmlElement} t The target of the event.
4193      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4194      */
4195
4196 //  DOM Mutation events
4197     /**
4198      * @event DOMSubtreeModified
4199      * Where supported. Fires when the subtree is modified.
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 DOMNodeInserted
4206      * Where supported. Fires when a node has been added as a child of another node.
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 DOMNodeRemoved
4213      * Where supported. Fires when a descendant node of the element is removed.
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      * @event DOMNodeRemovedFromDocument
4220      * Where supported. Fires when a node is being removed from a document.
4221      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4222      * @param {HtmlElement} t The target of the event.
4223      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4224      */
4225     /**
4226      * @event DOMNodeInsertedIntoDocument
4227      * Where supported. Fires when a node is being inserted into a document.
4228      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4229      * @param {HtmlElement} t The target of the event.
4230      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4231      */
4232     /**
4233      * @event DOMAttrModified
4234      * Where supported. Fires when an attribute has been modified.
4235      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4236      * @param {HtmlElement} t The target of the event.
4237      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4238      */
4239     /**
4240      * @event DOMCharacterDataModified
4241      * Where supported. Fires when the character data has been modified.
4242      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4243      * @param {HtmlElement} t The target of the event.
4244      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4245      */
4246
4247     /**
4248      * The default unit to append to CSS values where a unit isn't provided (defaults to px).
4249      * @type String
4250      */
4251     defaultUnit : "px",
4252
4253     /**
4254      * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
4255      * @param {String} selector The simple selector to test
4256      * @return {Boolean} True if this element matches the selector, else false
4257      */
4258     is : function(simpleSelector){
4259         return Ext.DomQuery.is(this.dom, simpleSelector);
4260     },
4261
4262     /**
4263      * Tries to focus the element. Any exceptions are caught and ignored.
4264      * @param {Number} defer (optional) Milliseconds to defer the focus
4265      * @return {Ext.Element} this
4266      */
4267     focus : function(defer, /* private */ dom) {
4268         var me = this,
4269             dom = dom || me.dom;
4270         try{
4271             if(Number(defer)){
4272                 me.focus.defer(defer, null, [null, dom]);
4273             }else{
4274                 dom.focus();
4275             }
4276         }catch(e){}
4277         return me;
4278     },
4279
4280     /**
4281      * Tries to blur the element. Any exceptions are caught and ignored.
4282      * @return {Ext.Element} this
4283      */
4284     blur : function() {
4285         try{
4286             this.dom.blur();
4287         }catch(e){}
4288         return this;
4289     },
4290
4291     /**
4292      * Returns the value of the "value" attribute
4293      * @param {Boolean} asNumber true to parse the value as a number
4294      * @return {String/Number}
4295      */
4296     getValue : function(asNumber){
4297         var val = this.dom.value;
4298         return asNumber ? parseInt(val, 10) : val;
4299     },
4300
4301     /**
4302      * Appends an event handler to this element.  The shorthand version {@link #on} is equivalent.
4303      * @param {String} eventName The name of event to handle.
4304      * @param {Function} fn The handler function the event invokes. This function is passed
4305      * the following parameters:<ul>
4306      * <li><b>evt</b> : EventObject<div class="sub-desc">The {@link Ext.EventObject EventObject} describing the event.</div></li>
4307      * <li><b>el</b> : HtmlElement<div class="sub-desc">The DOM element which was the target of the event.
4308      * Note that this may be filtered by using the <tt>delegate</tt> option.</div></li>
4309      * <li><b>o</b> : Object<div class="sub-desc">The options object from the addListener call.</div></li>
4310      * </ul>
4311      * @param {Object} scope (optional) The scope (<code><b>this</b></code> reference) in which the handler function is executed.
4312      * <b>If omitted, defaults to this Element.</b>.
4313      * @param {Object} options (optional) An object containing handler configuration properties.
4314      * This may contain any of the following properties:<ul>
4315      * <li><b>scope</b> Object : <div class="sub-desc">The scope (<code><b>this</b></code> reference) in which the handler function is executed.
4316      * <b>If omitted, defaults to this Element.</b></div></li>
4317      * <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>
4318      * <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>
4319      * <li><b>preventDefault</b> Boolean: <div class="sub-desc">True to prevent the default action</div></li>
4320      * <li><b>stopPropagation</b> Boolean: <div class="sub-desc">True to prevent event propagation</div></li>
4321      * <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>
4322      * <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>
4323      * <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>
4324      * <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>
4325      * <li><b>buffer</b> Number: <div class="sub-desc">Causes the handler to be scheduled to run in an {@link Ext.util.DelayedTask} delayed
4326      * by the specified number of milliseconds. If the event fires again within that time, the original
4327      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</div></li>
4328      * </ul><br>
4329      * <p>
4330      * <b>Combining Options</b><br>
4331      * In the following examples, the shorthand form {@link #on} is used rather than the more verbose
4332      * addListener.  The two are equivalent.  Using the options argument, it is possible to combine different
4333      * types of listeners:<br>
4334      * <br>
4335      * A delayed, one-time listener that auto stops the event and adds a custom argument (forumId) to the
4336      * options object. The options object is available as the third parameter in the handler function.<div style="margin: 5px 20px 20px;">
4337      * Code:<pre><code>
4338 el.on('click', this.onClick, this, {
4339     single: true,
4340     delay: 100,
4341     stopEvent : true,
4342     forumId: 4
4343 });</code></pre></p>
4344      * <p>
4345      * <b>Attaching multiple handlers in 1 call</b><br>
4346      * The method also allows for a single argument to be passed which is a config object containing properties
4347      * which specify multiple handlers.</p>
4348      * <p>
4349      * Code:<pre><code>
4350 el.on({
4351     'click' : {
4352         fn: this.onClick,
4353         scope: this,
4354         delay: 100
4355     },
4356     'mouseover' : {
4357         fn: this.onMouseOver,
4358         scope: this
4359     },
4360     'mouseout' : {
4361         fn: this.onMouseOut,
4362         scope: this
4363     }
4364 });</code></pre>
4365      * <p>
4366      * Or a shorthand syntax:<br>
4367      * Code:<pre><code></p>
4368 el.on({
4369     'click' : this.onClick,
4370     'mouseover' : this.onMouseOver,
4371     'mouseout' : this.onMouseOut,
4372     scope: this
4373 });
4374      * </code></pre></p>
4375      * <p><b>delegate</b></p>
4376      * <p>This is a configuration option that you can pass along when registering a handler for
4377      * an event to assist with event delegation. Event delegation is a technique that is used to
4378      * reduce memory consumption and prevent exposure to memory-leaks. By registering an event
4379      * for a container element as opposed to each element within a container. By setting this
4380      * configuration option to a simple selector, the target element will be filtered to look for
4381      * a descendant of the target.
4382      * For example:<pre><code>
4383 // using this markup:
4384 &lt;div id='elId'>
4385     &lt;p id='p1'>paragraph one&lt;/p>
4386     &lt;p id='p2' class='clickable'>paragraph two&lt;/p>
4387     &lt;p id='p3'>paragraph three&lt;/p>
4388 &lt;/div>
4389 // utilize event delegation to registering just one handler on the container element:
4390 el = Ext.get('elId');
4391 el.on(
4392     'click',
4393     function(e,t) {
4394         // handle click
4395         console.info(t.id); // 'p2'
4396     },
4397     this,
4398     {
4399         // filter the target element to be a descendant with the class 'clickable'
4400         delegate: '.clickable'
4401     }
4402 );
4403      * </code></pre></p>
4404      * @return {Ext.Element} this
4405      */
4406     addListener : function(eventName, fn, scope, options){
4407         Ext.EventManager.on(this.dom,  eventName, fn, scope || this, options);
4408         return this;
4409     },
4410
4411     /**
4412      * Removes an event handler from this element.  The shorthand version {@link #un} is equivalent.
4413      * <b>Note</b>: if a <i>scope</i> was explicitly specified when {@link #addListener adding} the
4414      * listener, the same scope must be specified here.
4415      * Example:
4416      * <pre><code>
4417 el.removeListener('click', this.handlerFn);
4418 // or
4419 el.un('click', this.handlerFn);
4420 </code></pre>
4421      * @param {String} eventName The name of the event from which to remove the handler.
4422      * @param {Function} fn The handler function to remove. <b>This must be a reference to the function passed into the {@link #addListener} call.</b>
4423      * @param {Object} scope If a scope (<b><code>this</code></b> reference) was specified when the listener was added,
4424      * then this must refer to the same object.
4425      * @return {Ext.Element} this
4426      */
4427     removeListener : function(eventName, fn, scope){
4428         Ext.EventManager.removeListener(this.dom,  eventName, fn, scope || this);
4429         return this;
4430     },
4431
4432     /**
4433      * Removes all previous added listeners from this element
4434      * @return {Ext.Element} this
4435      */
4436     removeAllListeners : function(){
4437         Ext.EventManager.removeAll(this.dom);
4438         return this;
4439     },
4440
4441     /**
4442      * Recursively removes all previous added listeners from this element and its children
4443      * @return {Ext.Element} this
4444      */
4445     purgeAllListeners : function() {
4446         Ext.EventManager.purgeElement(this, true);
4447         return this;
4448     },
4449     /**
4450      * @private Test if size has a unit, otherwise appends the default
4451      */
4452     addUnits : function(size){
4453         if(size === "" || size == "auto" || size === undefined){
4454             size = size || '';
4455         } else if(!isNaN(size) || !unitPattern.test(size)){
4456             size = size + (this.defaultUnit || 'px');
4457         }
4458         return size;
4459     },
4460
4461     /**
4462      * <p>Updates the <a href="http://developer.mozilla.org/en/DOM/element.innerHTML">innerHTML</a> of this Element
4463      * 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>
4464      * <p>Updating innerHTML of an element will <b>not</b> execute embedded <tt>&lt;script></tt> elements. This is a browser restriction.</p>
4465      * @param {Mixed} options. Either a sring containing the URL from which to load the HTML, or an {@link Ext.Ajax#request} options object specifying
4466      * exactly how to request the HTML.
4467      * @return {Ext.Element} this
4468      */
4469     load : function(url, params, cb){
4470         Ext.Ajax.request(Ext.apply({
4471             params: params,
4472             url: url.url || url,
4473             callback: cb,
4474             el: this.dom,
4475             indicatorText: url.indicatorText || ''
4476         }, Ext.isObject(url) ? url : {}));
4477         return this;
4478     },
4479
4480     /**
4481      * Tests various css rules/browsers to determine if this element uses a border box
4482      * @return {Boolean}
4483      */
4484     isBorderBox : function(){
4485         return noBoxAdjust[(this.dom.tagName || "").toLowerCase()] || Ext.isBorderBox;
4486     },
4487
4488     /**
4489      * <p>Removes this element's dom reference.  Note that event and cache removal is handled at {@link Ext#removeNode}</p>
4490      */
4491     remove : function(){
4492         var me = this,
4493             dom = me.dom;
4494
4495         if (dom) {
4496             delete me.dom;
4497             Ext.removeNode(dom);
4498         }
4499     },
4500
4501     /**
4502      * Sets up event handlers to call the passed functions when the mouse is moved into and out of the Element.
4503      * @param {Function} overFn The function to call when the mouse enters the Element.
4504      * @param {Function} outFn The function to call when the mouse leaves the Element.
4505      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the functions are executed. Defaults to the Element's DOM element.
4506      * @param {Object} options (optional) Options for the listener. See {@link Ext.util.Observable#addListener the <tt>options</tt> parameter}.
4507      * @return {Ext.Element} this
4508      */
4509     hover : function(overFn, outFn, scope, options){
4510         var me = this;
4511         me.on('mouseenter', overFn, scope || me.dom, options);
4512         me.on('mouseleave', outFn, scope || me.dom, options);
4513         return me;
4514     },
4515
4516     /**
4517      * Returns true if this element is an ancestor of the passed element
4518      * @param {HTMLElement/String} el The element to check
4519      * @return {Boolean} True if this element is an ancestor of el, else false
4520      */
4521     contains : function(el){
4522         return !el ? false : Ext.lib.Dom.isAncestor(this.dom, el.dom ? el.dom : el);
4523     },
4524
4525     /**
4526      * Returns the value of a namespaced attribute from the element's underlying DOM node.
4527      * @param {String} namespace The namespace in which to look for the attribute
4528      * @param {String} name The attribute name
4529      * @return {String} The attribute value
4530      * @deprecated
4531      */
4532     getAttributeNS : function(ns, name){
4533         return this.getAttribute(name, ns);
4534     },
4535
4536     /**
4537      * Returns the value of an attribute from the element's underlying DOM node.
4538      * @param {String} name The attribute name
4539      * @param {String} namespace (optional) The namespace in which to look for the attribute
4540      * @return {String} The attribute value
4541      */
4542     getAttribute : Ext.isIE ? function(name, ns){
4543         var d = this.dom,
4544             type = typeof d[ns + ":" + name];
4545
4546         if(['undefined', 'unknown'].indexOf(type) == -1){
4547             return d[ns + ":" + name];
4548         }
4549         return d[name];
4550     } : function(name, ns){
4551         var d = this.dom;
4552         return d.getAttributeNS(ns, name) || d.getAttribute(ns + ":" + name) || d.getAttribute(name) || d[name];
4553     },
4554
4555     /**
4556     * Update the innerHTML of this element
4557     * @param {String} html The new HTML
4558     * @return {Ext.Element} this
4559      */
4560     update : function(html) {
4561         if (this.dom) {
4562             this.dom.innerHTML = html;
4563         }
4564         return this;
4565     }
4566 };
4567
4568 var ep = El.prototype;
4569
4570 El.addMethods = function(o){
4571    Ext.apply(ep, o);
4572 };
4573
4574 /**
4575  * Appends an event handler (shorthand for {@link #addListener}).
4576  * @param {String} eventName The name of event to handle.
4577  * @param {Function} fn The handler function the event invokes.
4578  * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the handler function is executed.
4579  * @param {Object} options (optional) An object containing standard {@link #addListener} options
4580  * @member Ext.Element
4581  * @method on
4582  */
4583 ep.on = ep.addListener;
4584
4585 /**
4586  * Removes an event handler from this element (see {@link #removeListener} for additional notes).
4587  * @param {String} eventName The name of the event from which to remove the handler.
4588  * @param {Function} fn The handler function to remove. <b>This must be a reference to the function passed into the {@link #addListener} call.</b>
4589  * @param {Object} scope If a scope (<b><code>this</code></b> reference) was specified when the listener was added,
4590  * then this must refer to the same object.
4591  * @return {Ext.Element} this
4592  * @member Ext.Element
4593  * @method un
4594  */
4595 ep.un = ep.removeListener;
4596
4597 /**
4598  * true to automatically adjust width and height settings for box-model issues (default to true)
4599  */
4600 ep.autoBoxAdjust = true;
4601
4602 // private
4603 var unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i,
4604     docEl;
4605
4606 /**
4607  * @private
4608  */
4609
4610 /**
4611  * Retrieves Ext.Element objects.
4612  * <p><b>This method does not retrieve {@link Ext.Component Component}s.</b> This method
4613  * retrieves Ext.Element objects which encapsulate DOM elements. To retrieve a Component by
4614  * its ID, use {@link Ext.ComponentMgr#get}.</p>
4615  * <p>Uses simple caching to consistently return the same object. Automatically fixes if an
4616  * object was recreated with the same id via AJAX or DOM.</p>
4617  * @param {Mixed} el The id of the node, a DOM Node or an existing Element.
4618  * @return {Element} The Element object (or null if no matching element was found)
4619  * @static
4620  * @member Ext.Element
4621  * @method get
4622  */
4623 El.get = function(el){
4624     var ex,
4625         elm,
4626         id;
4627     if(!el){ return null; }
4628     if (typeof el == "string") { // element id
4629         if (!(elm = DOC.getElementById(el))) {
4630             return null;
4631         }
4632         if (EC[el] && EC[el].el) {
4633             ex = EC[el].el;
4634             ex.dom = elm;
4635         } else {
4636             ex = El.addToCache(new El(elm));
4637         }
4638         return ex;
4639     } else if (el.tagName) { // dom element
4640         if(!(id = el.id)){
4641             id = Ext.id(el);
4642         }
4643         if (EC[id] && EC[id].el) {
4644             ex = EC[id].el;
4645             ex.dom = el;
4646         } else {
4647             ex = El.addToCache(new El(el));
4648         }
4649         return ex;
4650     } else if (el instanceof El) {
4651         if(el != docEl){
4652             // refresh dom element in case no longer valid,
4653             // catch case where it hasn't been appended
4654
4655             // If an el instance is passed, don't pass to getElementById without some kind of id
4656             if (Ext.isIE && (el.id == undefined || el.id == '')) {
4657                 el.dom = el.dom;
4658             } else {
4659                 el.dom = DOC.getElementById(el.id) || el.dom;
4660             }
4661         }
4662         return el;
4663     } else if(el.isComposite) {
4664         return el;
4665     } else if(Ext.isArray(el)) {
4666         return El.select(el);
4667     } else if(el == DOC) {
4668         // create a bogus element object representing the document object
4669         if(!docEl){
4670             var f = function(){};
4671             f.prototype = El.prototype;
4672             docEl = new f();
4673             docEl.dom = DOC;
4674         }
4675         return docEl;
4676     }
4677     return null;
4678 };
4679
4680 El.addToCache = function(el, id){
4681     id = id || el.id;
4682     EC[id] = {
4683         el:  el,
4684         data: {},
4685         events: {}
4686     };
4687     return el;
4688 };
4689
4690 // private method for getting and setting element data
4691 El.data = function(el, key, value){
4692     el = El.get(el);
4693     if (!el) {
4694         return null;
4695     }
4696     var c = EC[el.id].data;
4697     if(arguments.length == 2){
4698         return c[key];
4699     }else{
4700         return (c[key] = value);
4701     }
4702 };
4703
4704 // private
4705 // Garbage collection - uncache elements/purge listeners on orphaned elements
4706 // so we don't hold a reference and cause the browser to retain them
4707 function garbageCollect(){
4708     if(!Ext.enableGarbageCollector){
4709         clearInterval(El.collectorThreadId);
4710     } else {
4711         var eid,
4712             el,
4713             d,
4714             o;
4715
4716         for(eid in EC){
4717             o = EC[eid];
4718             if(o.skipGC){
4719                 continue;
4720             }
4721             el = o.el;
4722             d = el.dom;
4723             // -------------------------------------------------------
4724             // Determining what is garbage:
4725             // -------------------------------------------------------
4726             // !d
4727             // dom node is null, definitely garbage
4728             // -------------------------------------------------------
4729             // !d.parentNode
4730             // no parentNode == direct orphan, definitely garbage
4731             // -------------------------------------------------------
4732             // !d.offsetParent && !document.getElementById(eid)
4733             // display none elements have no offsetParent so we will
4734             // also try to look it up by it's id. However, check
4735             // offsetParent first so we don't do unneeded lookups.
4736             // This enables collection of elements that are not orphans
4737             // directly, but somewhere up the line they have an orphan
4738             // parent.
4739             // -------------------------------------------------------
4740             if(!d || !d.parentNode || (!d.offsetParent && !DOC.getElementById(eid))){
4741                 if(Ext.enableListenerCollection){
4742                     Ext.EventManager.removeAll(d);
4743                 }
4744                 delete EC[eid];
4745             }
4746         }
4747         // Cleanup IE Object leaks
4748         if (Ext.isIE) {
4749             var t = {};
4750             for (eid in EC) {
4751                 t[eid] = EC[eid];
4752             }
4753             EC = Ext.elCache = t;
4754         }
4755     }
4756 }
4757 El.collectorThreadId = setInterval(garbageCollect, 30000);
4758
4759 var flyFn = function(){};
4760 flyFn.prototype = El.prototype;
4761
4762 // dom is optional
4763 El.Flyweight = function(dom){
4764     this.dom = dom;
4765 };
4766
4767 El.Flyweight.prototype = new flyFn();
4768 El.Flyweight.prototype.isFlyweight = true;
4769 El._flyweights = {};
4770
4771 /**
4772  * <p>Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
4773  * the dom node can be overwritten by other code. Shorthand of {@link Ext.Element#fly}</p>
4774  * <p>Use this to make one-time references to DOM elements which are not going to be accessed again either by
4775  * application code, or by Ext's classes. If accessing an element which will be processed regularly, then {@link Ext#get}
4776  * will be more appropriate to take advantage of the caching provided by the Ext.Element class.</p>
4777  * @param {String/HTMLElement} el The dom node or id
4778  * @param {String} named (optional) Allows for creation of named reusable flyweights to prevent conflicts
4779  * (e.g. internally Ext uses "_global")
4780  * @return {Element} The shared Element object (or null if no matching element was found)
4781  * @member Ext.Element
4782  * @method fly
4783  */
4784 El.fly = function(el, named){
4785     var ret = null;
4786     named = named || '_global';
4787
4788     if (el = Ext.getDom(el)) {
4789         (El._flyweights[named] = El._flyweights[named] || new El.Flyweight()).dom = el;
4790         ret = El._flyweights[named];
4791     }
4792     return ret;
4793 };
4794
4795 /**
4796  * Retrieves Ext.Element objects.
4797  * <p><b>This method does not retrieve {@link Ext.Component Component}s.</b> This method
4798  * retrieves Ext.Element objects which encapsulate DOM elements. To retrieve a Component by
4799  * its ID, use {@link Ext.ComponentMgr#get}.</p>
4800  * <p>Uses simple caching to consistently return the same object. Automatically fixes if an
4801  * object was recreated with the same id via AJAX or DOM.</p>
4802  * Shorthand of {@link Ext.Element#get}
4803  * @param {Mixed} el The id of the node, a DOM Node or an existing Element.
4804  * @return {Element} The Element object (or null if no matching element was found)
4805  * @member Ext
4806  * @method get
4807  */
4808 Ext.get = El.get;
4809
4810 /**
4811  * <p>Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
4812  * the dom node can be overwritten by other code. Shorthand of {@link Ext.Element#fly}</p>
4813  * <p>Use this to make one-time references to DOM elements which are not going to be accessed again either by
4814  * application code, or by Ext's classes. If accessing an element which will be processed regularly, then {@link Ext#get}
4815  * will be more appropriate to take advantage of the caching provided by the Ext.Element class.</p>
4816  * @param {String/HTMLElement} el The dom node or id
4817  * @param {String} named (optional) Allows for creation of named reusable flyweights to prevent conflicts
4818  * (e.g. internally Ext uses "_global")
4819  * @return {Element} The shared Element object (or null if no matching element was found)
4820  * @member Ext
4821  * @method fly
4822  */
4823 Ext.fly = El.fly;
4824
4825 // speedy lookup for elements never to box adjust
4826 var noBoxAdjust = Ext.isStrict ? {
4827     select:1
4828 } : {
4829     input:1, select:1, textarea:1
4830 };
4831 if(Ext.isIE || Ext.isGecko){
4832     noBoxAdjust['button'] = 1;
4833 }
4834
4835 })();
4836 /**
4837  * @class Ext.Element
4838  */
4839 Ext.Element.addMethods({
4840     /**
4841      * Stops the specified event(s) from bubbling and optionally prevents the default action
4842      * @param {String/Array} eventName an event / array of events to stop from bubbling
4843      * @param {Boolean} preventDefault (optional) true to prevent the default action too
4844      * @return {Ext.Element} this
4845      */
4846     swallowEvent : function(eventName, preventDefault){
4847         var me = this;
4848         function fn(e){
4849             e.stopPropagation();
4850             if(preventDefault){
4851                 e.preventDefault();
4852             }
4853         }
4854         if(Ext.isArray(eventName)){
4855             Ext.each(eventName, function(e) {
4856                  me.on(e, fn);
4857             });
4858             return me;
4859         }
4860         me.on(eventName, fn);
4861         return me;
4862     },
4863
4864     /**
4865      * Create an event handler on this element such that when the event fires and is handled by this element,
4866      * it will be relayed to another object (i.e., fired again as if it originated from that object instead).
4867      * @param {String} eventName The type of event to relay
4868      * @param {Object} object Any object that extends {@link Ext.util.Observable} that will provide the context
4869      * for firing the relayed event
4870      */
4871     relayEvent : function(eventName, observable){
4872         this.on(eventName, function(e){
4873             observable.fireEvent(eventName, e);
4874         });
4875     },
4876
4877     /**
4878      * Removes worthless text nodes
4879      * @param {Boolean} forceReclean (optional) By default the element
4880      * keeps track if it has been cleaned already so
4881      * you can call this over and over. However, if you update the element and
4882      * need to force a reclean, you can pass true.
4883      */
4884     clean : function(forceReclean){
4885         var me = this,
4886             dom = me.dom,
4887             n = dom.firstChild,
4888             ni = -1;
4889
4890         if(Ext.Element.data(dom, 'isCleaned') && forceReclean !== true){
4891             return me;
4892         }
4893
4894         while(n){
4895             var nx = n.nextSibling;
4896             if(n.nodeType == 3 && !/\S/.test(n.nodeValue)){
4897                 dom.removeChild(n);
4898             }else{
4899                 n.nodeIndex = ++ni;
4900             }
4901             n = nx;
4902         }
4903         Ext.Element.data(dom, 'isCleaned', true);
4904         return me;
4905     },
4906
4907     /**
4908      * Direct access to the Updater {@link Ext.Updater#update} method. The method takes the same object
4909      * parameter as {@link Ext.Updater#update}
4910      * @return {Ext.Element} this
4911      */
4912     load : function(){
4913         var um = this.getUpdater();
4914         um.update.apply(um, arguments);
4915         return this;
4916     },
4917
4918     /**
4919     * Gets this element's {@link Ext.Updater Updater}
4920     * @return {Ext.Updater} The Updater
4921     */
4922     getUpdater : function(){
4923         return this.updateManager || (this.updateManager = new Ext.Updater(this));
4924     },
4925
4926     /**
4927     * Update the innerHTML of this element, optionally searching for and processing scripts
4928     * @param {String} html The new HTML
4929     * @param {Boolean} loadScripts (optional) True to look for and process scripts (defaults to false)
4930     * @param {Function} callback (optional) For async script loading you can be notified when the update completes
4931     * @return {Ext.Element} this
4932      */
4933     update : function(html, loadScripts, callback){
4934         if (!this.dom) {
4935             return this;
4936         }
4937         html = html || "";
4938
4939         if(loadScripts !== true){
4940             this.dom.innerHTML = html;
4941             if(typeof callback == 'function'){
4942                 callback();
4943             }
4944             return this;
4945         }
4946
4947         var id = Ext.id(),
4948             dom = this.dom;
4949
4950         html += '<span id="' + id + '"></span>';
4951
4952         Ext.lib.Event.onAvailable(id, function(){
4953             var DOC = document,
4954                 hd = DOC.getElementsByTagName("head")[0],
4955                 re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig,
4956                 srcRe = /\ssrc=([\'\"])(.*?)\1/i,
4957                 typeRe = /\stype=([\'\"])(.*?)\1/i,
4958                 match,
4959                 attrs,
4960                 srcMatch,
4961                 typeMatch,
4962                 el,
4963                 s;
4964
4965             while((match = re.exec(html))){
4966                 attrs = match[1];
4967                 srcMatch = attrs ? attrs.match(srcRe) : false;
4968                 if(srcMatch && srcMatch[2]){
4969                    s = DOC.createElement("script");
4970                    s.src = srcMatch[2];
4971                    typeMatch = attrs.match(typeRe);
4972                    if(typeMatch && typeMatch[2]){
4973                        s.type = typeMatch[2];
4974                    }
4975                    hd.appendChild(s);
4976                 }else if(match[2] && match[2].length > 0){
4977                     if(window.execScript) {
4978                        window.execScript(match[2]);
4979                     } else {
4980                        window.eval(match[2]);
4981                     }
4982                 }
4983             }
4984             el = DOC.getElementById(id);
4985             if(el){Ext.removeNode(el);}
4986             if(typeof callback == 'function'){
4987                 callback();
4988             }
4989         });
4990         dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
4991         return this;
4992     },
4993
4994     // inherit docs, overridden so we can add removeAnchor
4995     removeAllListeners : function(){
4996         this.removeAnchor();
4997         Ext.EventManager.removeAll(this.dom);
4998         return this;
4999     },
5000
5001     /**
5002      * Creates a proxy element of this element
5003      * @param {String/Object} config The class name of the proxy element or a DomHelper config object
5004      * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
5005      * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
5006      * @return {Ext.Element} The new proxy element
5007      */
5008     createProxy : function(config, renderTo, matchBox){
5009         config = (typeof config == 'object') ? config : {tag : "div", cls: config};
5010
5011         var me = this,
5012             proxy = renderTo ? Ext.DomHelper.append(renderTo, config, true) :
5013                                Ext.DomHelper.insertBefore(me.dom, config, true);
5014
5015         if(matchBox && me.setBox && me.getBox){ // check to make sure Element.position.js is loaded
5016            proxy.setBox(me.getBox());
5017         }
5018         return proxy;
5019     }
5020 });
5021
5022 Ext.Element.prototype.getUpdateManager = Ext.Element.prototype.getUpdater;
5023 /**
5024  * @class Ext.Element
5025  */
5026 Ext.Element.addMethods({
5027     /**
5028      * Gets the x,y coordinates specified by the anchor position on the element.
5029      * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo}
5030      * for details on supported anchor positions.
5031      * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead
5032      * of page coordinates
5033      * @param {Object} size (optional) An object containing the size to use for calculating anchor position
5034      * {width: (target width), height: (target height)} (defaults to the element's current size)
5035      * @return {Array} [x, y] An array containing the element's x and y coordinates
5036      */
5037     getAnchorXY : function(anchor, local, s){
5038         //Passing a different size is useful for pre-calculating anchors,
5039         //especially for anchored animations that change the el size.
5040                 anchor = (anchor || "tl").toLowerCase();
5041         s = s || {};
5042         
5043         var me = this,        
5044                 vp = me.dom == document.body || me.dom == document,
5045                 w = s.width || vp ? Ext.lib.Dom.getViewWidth() : me.getWidth(),
5046                 h = s.height || vp ? Ext.lib.Dom.getViewHeight() : me.getHeight(),                              
5047                 xy,             
5048                 r = Math.round,
5049                 o = me.getXY(),
5050                 scroll = me.getScroll(),
5051                 extraX = vp ? scroll.left : !local ? o[0] : 0,
5052                 extraY = vp ? scroll.top : !local ? o[1] : 0,
5053                 hash = {
5054                         c  : [r(w * 0.5), r(h * 0.5)],
5055                         t  : [r(w * 0.5), 0],
5056                         l  : [0, r(h * 0.5)],
5057                         r  : [w, r(h * 0.5)],
5058                         b  : [r(w * 0.5), h],
5059                         tl : [0, 0],    
5060                         bl : [0, h],
5061                         br : [w, h],
5062                         tr : [w, 0]
5063                 };
5064         
5065         xy = hash[anchor];      
5066         return [xy[0] + extraX, xy[1] + extraY]; 
5067     },
5068
5069     /**
5070      * Anchors an element to another element and realigns it when the window is resized.
5071      * @param {Mixed} element The element to align to.
5072      * @param {String} position The position to align to.
5073      * @param {Array} offsets (optional) Offset the positioning by [x, y]
5074      * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
5075      * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
5076      * is a number, it is used as the buffer delay (defaults to 50ms).
5077      * @param {Function} callback The function to call after the animation finishes
5078      * @return {Ext.Element} this
5079      */
5080     anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){        
5081             var me = this,
5082             dom = me.dom,
5083             scroll = !Ext.isEmpty(monitorScroll),
5084             action = function(){
5085                 Ext.fly(dom).alignTo(el, alignment, offsets, animate);
5086                 Ext.callback(callback, Ext.fly(dom));
5087             },
5088             anchor = this.getAnchor();
5089             
5090         // previous listener anchor, remove it
5091         this.removeAnchor();
5092         Ext.apply(anchor, {
5093             fn: action,
5094             scroll: scroll
5095         });
5096
5097         Ext.EventManager.onWindowResize(action, null);
5098         
5099         if(scroll){
5100             Ext.EventManager.on(window, 'scroll', action, null,
5101                 {buffer: !isNaN(monitorScroll) ? monitorScroll : 50});
5102         }
5103         action.call(me); // align immediately
5104         return me;
5105     },
5106     
5107     /**
5108      * Remove any anchor to this element. See {@link #anchorTo}.
5109      * @return {Ext.Element} this
5110      */
5111     removeAnchor : function(){
5112         var me = this,
5113             anchor = this.getAnchor();
5114             
5115         if(anchor && anchor.fn){
5116             Ext.EventManager.removeResizeListener(anchor.fn);
5117             if(anchor.scroll){
5118                 Ext.EventManager.un(window, 'scroll', anchor.fn);
5119             }
5120             delete anchor.fn;
5121         }
5122         return me;
5123     },
5124     
5125     // private
5126     getAnchor : function(){
5127         var data = Ext.Element.data,
5128             dom = this.dom;
5129             if (!dom) {
5130                 return;
5131             }
5132             var anchor = data(dom, '_anchor');
5133             
5134         if(!anchor){
5135             anchor = data(dom, '_anchor', {});
5136         }
5137         return anchor;
5138     },
5139
5140     /**
5141      * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
5142      * supported position values.
5143      * @param {Mixed} element The element to align to.
5144      * @param {String} position (optional, defaults to "tl-bl?") The position to align to.
5145      * @param {Array} offsets (optional) Offset the positioning by [x, y]
5146      * @return {Array} [x, y]
5147      */
5148     getAlignToXY : function(el, p, o){      
5149         el = Ext.get(el);
5150         
5151         if(!el || !el.dom){
5152             throw "Element.alignToXY with an element that doesn't exist";
5153         }
5154         
5155         o = o || [0,0];
5156         p = (!p || p == "?" ? "tl-bl?" : (!/-/.test(p) && p !== "" ? "tl-" + p : p || "tl-bl")).toLowerCase();       
5157                 
5158         var me = this,
5159                 d = me.dom,
5160                 a1,
5161                 a2,
5162                 x,
5163                 y,
5164                 //constrain the aligned el to viewport if necessary
5165                 w,
5166                 h,
5167                 r,
5168                 dw = Ext.lib.Dom.getViewWidth() -10, // 10px of margin for ie
5169                 dh = Ext.lib.Dom.getViewHeight()-10, // 10px of margin for ie
5170                 p1y,
5171                 p1x,            
5172                 p2y,
5173                 p2x,
5174                 swapY,
5175                 swapX,
5176                 doc = document,
5177                 docElement = doc.documentElement,
5178                 docBody = doc.body,
5179                 scrollX = (docElement.scrollLeft || docBody.scrollLeft || 0)+5,
5180                 scrollY = (docElement.scrollTop || docBody.scrollTop || 0)+5,
5181                 c = false, //constrain to viewport
5182                 p1 = "", 
5183                 p2 = "",
5184                 m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
5185         
5186         if(!m){
5187            throw "Element.alignTo with an invalid alignment " + p;
5188         }
5189         
5190         p1 = m[1]; 
5191         p2 = m[2]; 
5192         c = !!m[3];
5193
5194         //Subtract the aligned el's internal xy from the target's offset xy
5195         //plus custom offset to get the aligned el's new offset xy
5196         a1 = me.getAnchorXY(p1, true);
5197         a2 = el.getAnchorXY(p2, false);
5198
5199         x = a2[0] - a1[0] + o[0];
5200         y = a2[1] - a1[1] + o[1];
5201
5202         if(c){    
5203                w = me.getWidth();
5204            h = me.getHeight();
5205            r = el.getRegion();       
5206            //If we are at a viewport boundary and the aligned el is anchored on a target border that is
5207            //perpendicular to the vp border, allow the aligned el to slide on that border,
5208            //otherwise swap the aligned el to the opposite border of the target.
5209            p1y = p1.charAt(0);
5210            p1x = p1.charAt(p1.length-1);
5211            p2y = p2.charAt(0);
5212            p2x = p2.charAt(p2.length-1);
5213            swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
5214            swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));          
5215            
5216
5217            if (x + w > dw + scrollX) {
5218                 x = swapX ? r.left-w : dw+scrollX-w;
5219            }
5220            if (x < scrollX) {
5221                x = swapX ? r.right : scrollX;
5222            }
5223            if (y + h > dh + scrollY) {
5224                 y = swapY ? r.top-h : dh+scrollY-h;
5225             }
5226            if (y < scrollY){
5227                y = swapY ? r.bottom : scrollY;
5228            }
5229         }
5230         return [x,y];
5231     },
5232
5233     /**
5234      * Aligns this element with another element relative to the specified anchor points. If the other element is the
5235      * document it aligns it to the viewport.
5236      * The position parameter is optional, and can be specified in any one of the following formats:
5237      * <ul>
5238      *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
5239      *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
5240      *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
5241      *       deprecated in favor of the newer two anchor syntax below</i>.</li>
5242      *   <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
5243      *       element's anchor point, and the second value is used as the target's anchor point.</li>
5244      * </ul>
5245      * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
5246      * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
5247      * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
5248      * that specified in order to enforce the viewport constraints.
5249      * Following are all of the supported anchor positions:
5250 <pre>
5251 Value  Description
5252 -----  -----------------------------
5253 tl     The top left corner (default)
5254 t      The center of the top edge
5255 tr     The top right corner
5256 l      The center of the left edge
5257 c      In the center of the element
5258 r      The center of the right edge
5259 bl     The bottom left corner
5260 b      The center of the bottom edge
5261 br     The bottom right corner
5262 </pre>
5263 Example Usage:
5264 <pre><code>
5265 // align el to other-el using the default positioning ("tl-bl", non-constrained)
5266 el.alignTo("other-el");
5267
5268 // align the top left corner of el with the top right corner of other-el (constrained to viewport)
5269 el.alignTo("other-el", "tr?");
5270
5271 // align the bottom right corner of el with the center left edge of other-el
5272 el.alignTo("other-el", "br-l?");
5273
5274 // align the center of el with the bottom left corner of other-el and
5275 // adjust the x position by -6 pixels (and the y position by 0)
5276 el.alignTo("other-el", "c-bl", [-6, 0]);
5277 </code></pre>
5278      * @param {Mixed} element The element to align to.
5279      * @param {String} position (optional, defaults to "tl-bl?") The position to align to.
5280      * @param {Array} offsets (optional) Offset the positioning by [x, y]
5281      * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
5282      * @return {Ext.Element} this
5283      */
5284     alignTo : function(element, position, offsets, animate){
5285             var me = this;
5286         return me.setXY(me.getAlignToXY(element, position, offsets),
5287                                 me.preanim && !!animate ? me.preanim(arguments, 3) : false);
5288     },
5289     
5290     // private ==>  used outside of core
5291     adjustForConstraints : function(xy, parent, offsets){
5292         return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
5293     },
5294
5295     // private ==>  used outside of core
5296     getConstrainToXY : function(el, local, offsets, proposedXY){   
5297             var os = {top:0, left:0, bottom:0, right: 0};
5298
5299         return function(el, local, offsets, proposedXY){
5300             el = Ext.get(el);
5301             offsets = offsets ? Ext.applyIf(offsets, os) : os;
5302
5303             var vw, vh, vx = 0, vy = 0;
5304             if(el.dom == document.body || el.dom == document){
5305                 vw =Ext.lib.Dom.getViewWidth();
5306                 vh = Ext.lib.Dom.getViewHeight();
5307             }else{
5308                 vw = el.dom.clientWidth;
5309                 vh = el.dom.clientHeight;
5310                 if(!local){
5311                     var vxy = el.getXY();
5312                     vx = vxy[0];
5313                     vy = vxy[1];
5314                 }
5315             }
5316
5317             var s = el.getScroll();
5318
5319             vx += offsets.left + s.left;
5320             vy += offsets.top + s.top;
5321
5322             vw -= offsets.right;
5323             vh -= offsets.bottom;
5324
5325             var vr = vx + vw,
5326                 vb = vy + vh,
5327                 xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]),
5328                 x = xy[0], y = xy[1],
5329                 offset = this.getConstrainOffset(),
5330                 w = this.dom.offsetWidth + offset, 
5331                 h = this.dom.offsetHeight + offset;
5332
5333             // only move it if it needs it
5334             var moved = false;
5335
5336             // first validate right/bottom
5337             if((x + w) > vr){
5338                 x = vr - w;
5339                 moved = true;
5340             }
5341             if((y + h) > vb){
5342                 y = vb - h;
5343                 moved = true;
5344             }
5345             // then make sure top/left isn't negative
5346             if(x < vx){
5347                 x = vx;
5348                 moved = true;
5349             }
5350             if(y < vy){
5351                 y = vy;
5352                 moved = true;
5353             }
5354             return moved ? [x, y] : false;
5355         };
5356     }(),
5357             
5358             
5359                 
5360 //         el = Ext.get(el);
5361 //         offsets = Ext.applyIf(offsets || {}, {top : 0, left : 0, bottom : 0, right : 0});
5362
5363 //         var  me = this,
5364 //              doc = document,
5365 //              s = el.getScroll(),
5366 //              vxy = el.getXY(),
5367 //              vx = offsets.left + s.left, 
5368 //              vy = offsets.top + s.top,               
5369 //              vw = -offsets.right, 
5370 //              vh = -offsets.bottom, 
5371 //              vr,
5372 //              vb,
5373 //              xy = proposedXY || (!local ? me.getXY() : [me.getLeft(true), me.getTop(true)]),
5374 //              x = xy[0],
5375 //              y = xy[1],
5376 //              w = me.dom.offsetWidth, h = me.dom.offsetHeight,
5377 //              moved = false; // only move it if it needs it
5378 //       
5379 //              
5380 //         if(el.dom == doc.body || el.dom == doc){
5381 //             vw += Ext.lib.Dom.getViewWidth();
5382 //             vh += Ext.lib.Dom.getViewHeight();
5383 //         }else{
5384 //             vw += el.dom.clientWidth;
5385 //             vh += el.dom.clientHeight;
5386 //             if(!local){                    
5387 //                 vx += vxy[0];
5388 //                 vy += vxy[1];
5389 //             }
5390 //         }
5391
5392 //         // first validate right/bottom
5393 //         if(x + w > vx + vw){
5394 //             x = vx + vw - w;
5395 //             moved = true;
5396 //         }
5397 //         if(y + h > vy + vh){
5398 //             y = vy + vh - h;
5399 //             moved = true;
5400 //         }
5401 //         // then make sure top/left isn't negative
5402 //         if(x < vx){
5403 //             x = vx;
5404 //             moved = true;
5405 //         }
5406 //         if(y < vy){
5407 //             y = vy;
5408 //             moved = true;
5409 //         }
5410 //         return moved ? [x, y] : false;
5411 //    },
5412
5413     // private, used internally
5414     getConstrainOffset : function(){
5415         return 0;
5416     },
5417     
5418     /**
5419     * Calculates the x, y to center this element on the screen
5420     * @return {Array} The x, y values [x, y]
5421     */
5422     getCenterXY : function(){
5423         return this.getAlignToXY(document, 'c-c');
5424     },
5425
5426     /**
5427     * Centers the Element in either the viewport, or another Element.
5428     * @param {Mixed} centerIn (optional) The element in which to center the element.
5429     */
5430     center : function(centerIn){
5431         return this.alignTo(centerIn || document, 'c-c');        
5432     }    
5433 });
5434 /**
5435  * @class Ext.Element
5436  */
5437 Ext.Element.addMethods(function(){
5438         var PARENTNODE = 'parentNode',
5439                 NEXTSIBLING = 'nextSibling',
5440                 PREVIOUSSIBLING = 'previousSibling',
5441                 DQ = Ext.DomQuery,
5442                 GET = Ext.get;
5443         
5444         return {
5445                 /**
5446              * 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)
5447              * @param {String} selector The simple selector to test
5448              * @param {Number/Mixed} maxDepth (optional) The max depth to search as a number or element (defaults to 50 || document.body)
5449              * @param {Boolean} returnEl (optional) True to return a Ext.Element object instead of DOM node
5450              * @return {HTMLElement} The matching DOM node (or null if no match was found)
5451              */
5452             findParent : function(simpleSelector, maxDepth, returnEl){
5453                 var p = this.dom,
5454                         b = document.body, 
5455                         depth = 0,                      
5456                         stopEl;         
5457             if(Ext.isGecko && Object.prototype.toString.call(p) == '[object XULElement]') {
5458                 return null;
5459             }
5460                 maxDepth = maxDepth || 50;
5461                 if (isNaN(maxDepth)) {
5462                     stopEl = Ext.getDom(maxDepth);
5463                     maxDepth = Number.MAX_VALUE;
5464                 }
5465                 while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
5466                     if(DQ.is(p, simpleSelector)){
5467                         return returnEl ? GET(p) : p;
5468                     }
5469                     depth++;
5470                     p = p.parentNode;
5471                 }
5472                 return null;
5473             },
5474         
5475             /**
5476              * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
5477              * @param {String} selector The simple selector to test
5478              * @param {Number/Mixed} maxDepth (optional) The max depth to
5479                     search as a number or element (defaults to 10 || document.body)
5480              * @param {Boolean} returnEl (optional) True to return a Ext.Element object instead of DOM node
5481              * @return {HTMLElement} The matching DOM node (or null if no match was found)
5482              */
5483             findParentNode : function(simpleSelector, maxDepth, returnEl){
5484                 var p = Ext.fly(this.dom.parentNode, '_internal');
5485                 return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
5486             },
5487         
5488             /**
5489              * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
5490              * This is a shortcut for findParentNode() that always returns an Ext.Element.
5491              * @param {String} selector The simple selector to test
5492              * @param {Number/Mixed} maxDepth (optional) The max depth to
5493                     search as a number or element (defaults to 10 || document.body)
5494              * @return {Ext.Element} The matching DOM node (or null if no match was found)
5495              */
5496             up : function(simpleSelector, maxDepth){
5497                 return this.findParentNode(simpleSelector, maxDepth, true);
5498             },
5499         
5500             /**
5501              * Creates a {@link Ext.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
5502              * @param {String} selector The CSS selector
5503              * @return {CompositeElement/CompositeElementLite} The composite element
5504              */
5505             select : function(selector){
5506                 return Ext.Element.select(selector, this.dom);
5507             },
5508         
5509             /**
5510              * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
5511              * @param {String} selector The CSS selector
5512              * @return {Array} An array of the matched nodes
5513              */
5514             query : function(selector){
5515                 return DQ.select(selector, this.dom);
5516             },
5517         
5518             /**
5519              * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
5520              * @param {String} selector The CSS selector
5521              * @param {Boolean} returnDom (optional) True to return the DOM node instead of Ext.Element (defaults to false)
5522              * @return {HTMLElement/Ext.Element} The child Ext.Element (or DOM node if returnDom = true)
5523              */
5524             child : function(selector, returnDom){
5525                 var n = DQ.selectNode(selector, this.dom);
5526                 return returnDom ? n : GET(n);
5527             },
5528         
5529             /**
5530              * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
5531              * @param {String} selector The CSS selector
5532              * @param {Boolean} returnDom (optional) True to return the DOM node instead of Ext.Element (defaults to false)
5533              * @return {HTMLElement/Ext.Element} The child Ext.Element (or DOM node if returnDom = true)
5534              */
5535             down : function(selector, returnDom){
5536                 var n = DQ.selectNode(" > " + selector, this.dom);
5537                 return returnDom ? n : GET(n);
5538             },
5539         
5540                  /**
5541              * Gets the parent node for this element, optionally chaining up trying to match a selector
5542              * @param {String} selector (optional) Find a parent node that matches the passed simple selector
5543              * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.Element
5544              * @return {Ext.Element/HTMLElement} The parent node or null
5545                  */
5546             parent : function(selector, returnDom){
5547                 return this.matchNode(PARENTNODE, PARENTNODE, selector, returnDom);
5548             },
5549         
5550              /**
5551              * Gets the next sibling, skipping text nodes
5552              * @param {String} selector (optional) Find the next sibling that matches the passed simple selector
5553              * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.Element
5554              * @return {Ext.Element/HTMLElement} The next sibling or null
5555                  */
5556             next : function(selector, returnDom){
5557                 return this.matchNode(NEXTSIBLING, NEXTSIBLING, selector, returnDom);
5558             },
5559         
5560             /**
5561              * Gets the previous sibling, skipping text nodes
5562              * @param {String} selector (optional) Find the previous sibling that matches the passed simple selector
5563              * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.Element
5564              * @return {Ext.Element/HTMLElement} The previous sibling or null
5565                  */
5566             prev : function(selector, returnDom){
5567                 return this.matchNode(PREVIOUSSIBLING, PREVIOUSSIBLING, selector, returnDom);
5568             },
5569         
5570         
5571             /**
5572              * Gets the first child, skipping text nodes
5573              * @param {String} selector (optional) Find the next sibling that matches the passed simple selector
5574              * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.Element
5575              * @return {Ext.Element/HTMLElement} The first child or null
5576                  */
5577             first : function(selector, returnDom){
5578                 return this.matchNode(NEXTSIBLING, 'firstChild', selector, returnDom);
5579             },
5580         
5581             /**
5582              * Gets the last child, skipping text nodes
5583              * @param {String} selector (optional) Find the previous sibling that matches the passed simple selector
5584              * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.Element
5585              * @return {Ext.Element/HTMLElement} The last child or null
5586                  */
5587             last : function(selector, returnDom){
5588                 return this.matchNode(PREVIOUSSIBLING, 'lastChild', selector, returnDom);
5589             },
5590             
5591             matchNode : function(dir, start, selector, returnDom){
5592                 var n = this.dom[start];
5593                 while(n){
5594                     if(n.nodeType == 1 && (!selector || DQ.is(n, selector))){
5595                         return !returnDom ? GET(n) : n;
5596                     }
5597                     n = n[dir];
5598                 }
5599                 return null;
5600             }   
5601     }
5602 }());/**
5603  * @class Ext.Element
5604  */
5605 Ext.Element.addMethods({
5606     /**
5607      * Creates a {@link Ext.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
5608      * @param {String} selector The CSS selector
5609      * @param {Boolean} unique (optional) True to create a unique Ext.Element for each child (defaults to false, which creates a single shared flyweight object)
5610      * @return {CompositeElement/CompositeElementLite} The composite element
5611      */
5612     select : function(selector, unique){
5613         return Ext.Element.select(selector, unique, this.dom);
5614     }
5615 });/**
5616  * @class Ext.Element
5617  */
5618 Ext.Element.addMethods(
5619 function() {
5620         var GETDOM = Ext.getDom,
5621                 GET = Ext.get,
5622                 DH = Ext.DomHelper;
5623         
5624         return {
5625             /**
5626              * Appends the passed element(s) to this element
5627              * @param {String/HTMLElement/Array/Element/CompositeElement} el
5628              * @return {Ext.Element} this
5629              */
5630             appendChild: function(el){        
5631                 return GET(el).appendTo(this);        
5632             },
5633         
5634             /**
5635              * Appends this element to the passed element
5636              * @param {Mixed} el The new parent element
5637              * @return {Ext.Element} this
5638              */
5639             appendTo: function(el){        
5640                 GETDOM(el).appendChild(this.dom);        
5641                 return this;
5642             },
5643         
5644             /**
5645              * Inserts this element before the passed element in the DOM
5646              * @param {Mixed} el The element before which this element will be inserted
5647              * @return {Ext.Element} this
5648              */
5649             insertBefore: function(el){                   
5650                 (el = GETDOM(el)).parentNode.insertBefore(this.dom, el);
5651                 return this;
5652             },
5653         
5654             /**
5655              * Inserts this element after the passed element in the DOM
5656              * @param {Mixed} el The element to insert after
5657              * @return {Ext.Element} this
5658              */
5659             insertAfter: function(el){
5660                 (el = GETDOM(el)).parentNode.insertBefore(this.dom, el.nextSibling);
5661                 return this;
5662             },
5663         
5664             /**
5665              * Inserts (or creates) an element (or DomHelper config) as the first child of this element
5666              * @param {Mixed/Object} el The id or element to insert or a DomHelper config to create and insert
5667              * @return {Ext.Element} The new child
5668              */
5669             insertFirst: function(el, returnDom){
5670             el = el || {};
5671             if(el.nodeType || el.dom || typeof el == 'string'){ // element
5672                 el = GETDOM(el);
5673                 this.dom.insertBefore(el, this.dom.firstChild);
5674                 return !returnDom ? GET(el) : el;
5675             }else{ // dh config
5676                 return this.createChild(el, this.dom.firstChild, returnDom);
5677             }
5678         },
5679         
5680             /**
5681              * Replaces the passed element with this element
5682              * @param {Mixed} el The element to replace
5683              * @return {Ext.Element} this
5684              */
5685             replace: function(el){
5686                 el = GET(el);
5687                 this.insertBefore(el);
5688                 el.remove();
5689                 return this;
5690             },
5691         
5692             /**
5693              * Replaces this element with the passed element
5694              * @param {Mixed/Object} el The new element or a DomHelper config of an element to create
5695              * @return {Ext.Element} this
5696              */
5697             replaceWith: function(el){
5698                     var me = this;
5699                 
5700             if(el.nodeType || el.dom || typeof el == 'string'){
5701                 el = GETDOM(el);
5702                 me.dom.parentNode.insertBefore(el, me.dom);
5703             }else{
5704                 el = DH.insertBefore(me.dom, el);
5705             }
5706                 
5707                 delete Ext.elCache[me.id];
5708                 Ext.removeNode(me.dom);      
5709                 me.id = Ext.id(me.dom = el);
5710                 Ext.Element.addToCache(me.isFlyweight ? new Ext.Element(me.dom) : me);     
5711             return me;
5712             },
5713             
5714                 /**
5715                  * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
5716                  * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
5717                  * automatically generated with the specified attributes.
5718                  * @param {HTMLElement} insertBefore (optional) a child element of this element
5719                  * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
5720                  * @return {Ext.Element} The new child element
5721                  */
5722                 createChild: function(config, insertBefore, returnDom){
5723                     config = config || {tag:'div'};
5724                     return insertBefore ? 
5725                            DH.insertBefore(insertBefore, config, returnDom !== true) :  
5726                            DH[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
5727                 },
5728                 
5729                 /**
5730                  * Creates and wraps this element with another element
5731                  * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
5732                  * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Ext.Element
5733                  * @return {HTMLElement/Element} The newly created wrapper element
5734                  */
5735                 wrap: function(config, returnDom){        
5736                     var newEl = DH.insertBefore(this.dom, config || {tag: "div"}, !returnDom);
5737                     newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
5738                     return newEl;
5739                 },
5740                 
5741                 /**
5742                  * Inserts an html fragment into this element
5743                  * @param {String} where Where to insert the html in relation to this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
5744                  * @param {String} html The HTML fragment
5745                  * @param {Boolean} returnEl (optional) True to return an Ext.Element (defaults to false)
5746                  * @return {HTMLElement/Ext.Element} The inserted node (or nearest related if more than 1 inserted)
5747                  */
5748                 insertHtml : function(where, html, returnEl){
5749                     var el = DH.insertHtml(where, this.dom, html);
5750                     return returnEl ? Ext.get(el) : el;
5751                 }
5752         }
5753 }());/**
5754  * @class Ext.Element
5755  */
5756 Ext.apply(Ext.Element.prototype, function() {
5757         var GETDOM = Ext.getDom,
5758                 GET = Ext.get,
5759                 DH = Ext.DomHelper;
5760         
5761         return {        
5762                 /**
5763              * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
5764              * @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.
5765              * @param {String} where (optional) 'before' or 'after' defaults to before
5766              * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Ext.Element
5767              * @return {Ext.Element} The inserted Element. If an array is passed, the last inserted element is returned.
5768              */
5769             insertSibling: function(el, where, returnDom){
5770                 var me = this,
5771                         rt,
5772                 isAfter = (where || 'before').toLowerCase() == 'after',
5773                 insertEl;
5774                         
5775                 if(Ext.isArray(el)){
5776                 insertEl = me;
5777                     Ext.each(el, function(e) {
5778                             rt = Ext.fly(insertEl, '_internal').insertSibling(e, where, returnDom);
5779                     if(isAfter){
5780                         insertEl = rt;
5781                     }
5782                     });
5783                     return rt;
5784                 }
5785                         
5786                 el = el || {};
5787                 
5788             if(el.nodeType || el.dom){
5789                 rt = me.dom.parentNode.insertBefore(GETDOM(el), isAfter ? me.dom.nextSibling : me.dom);
5790                 if (!returnDom) {
5791                     rt = GET(rt);
5792                 }
5793             }else{
5794                 if (isAfter && !me.dom.nextSibling) {
5795                     rt = DH.append(me.dom.parentNode, el, !returnDom);
5796                 } else {                    
5797                     rt = DH[isAfter ? 'insertAfter' : 'insertBefore'](me.dom, el, !returnDom);
5798                 }
5799             }
5800                 return rt;
5801             }
5802     };
5803 }());/**
5804  * @class Ext.Element
5805  */
5806 Ext.Element.addMethods(function(){
5807     // local style camelizing for speed
5808     var propCache = {},
5809         camelRe = /(-[a-z])/gi,
5810         view = document.defaultView,
5811         propFloat = Ext.isIE ? 'styleFloat' : 'cssFloat',
5812         opacityRe = /alpha\(opacity=(.*)\)/i,
5813         trimRe = /^\s+|\s+$/g,
5814         spacesRe = /\s+/,
5815         wordsRe = /\w/g,
5816         PADDING = "padding",
5817         MARGIN = "margin",
5818         BORDER = "border",
5819         LEFT = "-left",
5820         RIGHT = "-right",
5821         TOP = "-top",
5822         BOTTOM = "-bottom",
5823         WIDTH = "-width",
5824         MATH = Math,
5825         HIDDEN = 'hidden',
5826         ISCLIPPED = 'isClipped',
5827         OVERFLOW = 'overflow',
5828         OVERFLOWX = 'overflow-x',
5829         OVERFLOWY = 'overflow-y',
5830         ORIGINALCLIP = 'originalClip',
5831         // special markup used throughout Ext when box wrapping elements
5832         borders = {l: BORDER + LEFT + WIDTH, r: BORDER + RIGHT + WIDTH, t: BORDER + TOP + WIDTH, b: BORDER + BOTTOM + WIDTH},
5833         paddings = {l: PADDING + LEFT, r: PADDING + RIGHT, t: PADDING + TOP, b: PADDING + BOTTOM},
5834         margins = {l: MARGIN + LEFT, r: MARGIN + RIGHT, t: MARGIN + TOP, b: MARGIN + BOTTOM},
5835         data = Ext.Element.data;
5836
5837
5838     // private
5839     function camelFn(m, a) {
5840         return a.charAt(1).toUpperCase();
5841     }
5842
5843     function chkCache(prop) {
5844         return propCache[prop] || (propCache[prop] = prop == 'float' ? propFloat : prop.replace(camelRe, camelFn));
5845     }
5846
5847     return {
5848         // private  ==> used by Fx
5849         adjustWidth : function(width) {
5850             var me = this;
5851             var isNum = (typeof width == "number");
5852             if(isNum && me.autoBoxAdjust && !me.isBorderBox()){
5853                width -= (me.getBorderWidth("lr") + me.getPadding("lr"));
5854             }
5855             return (isNum && width < 0) ? 0 : width;
5856         },
5857
5858         // private   ==> used by Fx
5859         adjustHeight : function(height) {
5860             var me = this;
5861             var isNum = (typeof height == "number");
5862             if(isNum && me.autoBoxAdjust && !me.isBorderBox()){
5863                height -= (me.getBorderWidth("tb") + me.getPadding("tb"));
5864             }
5865             return (isNum && height < 0) ? 0 : height;
5866         },
5867
5868
5869         /**
5870          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
5871          * @param {String/Array} className The CSS class to add, or an array of classes
5872          * @return {Ext.Element} this
5873          */
5874         addClass : function(className){
5875             var me = this,
5876                 i,
5877                 len,
5878                 v,
5879                 cls = [];
5880             // Separate case is for speed
5881             if (!Ext.isArray(className)) {
5882                 if (typeof className == 'string' && !this.hasClass(className)) {
5883                     me.dom.className += " " + className;
5884                 }
5885             }
5886             else {
5887                 for (i = 0, len = className.length; i < len; i++) {
5888                     v = className[i];
5889                     if (typeof v == 'string' && (' ' + me.dom.className + ' ').indexOf(' ' + v + ' ') == -1) {
5890                         cls.push(v);
5891                     }
5892                 }
5893                 if (cls.length) {
5894                     me.dom.className += " " + cls.join(" ");
5895                 }
5896             }
5897             return me;
5898         },
5899
5900         /**
5901          * Removes one or more CSS classes from the element.
5902          * @param {String/Array} className The CSS class to remove, or an array of classes
5903          * @return {Ext.Element} this
5904          */
5905         removeClass : function(className){
5906             var me = this,
5907                 i,
5908                 idx,
5909                 len,
5910                 cls,
5911                 elClasses;
5912             if (!Ext.isArray(className)){
5913                 className = [className];
5914             }
5915             if (me.dom && me.dom.className) {
5916                 elClasses = me.dom.className.replace(trimRe, '').split(spacesRe);
5917                 for (i = 0, len = className.length; i < len; i++) {
5918                     cls = className[i];
5919                     if (typeof cls == 'string') {
5920                         cls = cls.replace(trimRe, '');
5921                         idx = elClasses.indexOf(cls);
5922                         if (idx != -1) {
5923                             elClasses.splice(idx, 1);
5924                         }
5925                     }
5926                 }
5927                 me.dom.className = elClasses.join(" ");
5928             }
5929             return me;
5930         },
5931
5932         /**
5933          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
5934          * @param {String/Array} className The CSS class to add, or an array of classes
5935          * @return {Ext.Element} this
5936          */
5937         radioClass : function(className){
5938             var cn = this.dom.parentNode.childNodes,
5939                 v,
5940                 i,
5941                 len;
5942             className = Ext.isArray(className) ? className : [className];
5943             for (i = 0, len = cn.length; i < len; i++) {
5944                 v = cn[i];
5945                 if (v && v.nodeType == 1) {
5946                     Ext.fly(v, '_internal').removeClass(className);
5947                 }
5948             };
5949             return this.addClass(className);
5950         },
5951
5952         /**
5953          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
5954          * @param {String} className The CSS class to toggle
5955          * @return {Ext.Element} this
5956          */
5957         toggleClass : function(className){
5958             return this.hasClass(className) ? this.removeClass(className) : this.addClass(className);
5959         },
5960
5961         /**
5962          * Checks if the specified CSS class exists on this element's DOM node.
5963          * @param {String} className The CSS class to check for
5964          * @return {Boolean} True if the class exists, else false
5965          */
5966         hasClass : function(className){
5967             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
5968         },
5969
5970         /**
5971          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
5972          * @param {String} oldClassName The CSS class to replace
5973          * @param {String} newClassName The replacement CSS class
5974          * @return {Ext.Element} this
5975          */
5976         replaceClass : function(oldClassName, newClassName){
5977             return this.removeClass(oldClassName).addClass(newClassName);
5978         },
5979
5980         isStyle : function(style, val) {
5981             return this.getStyle(style) == val;
5982         },
5983
5984         /**
5985          * Normalizes currentStyle and computedStyle.
5986          * @param {String} property The style property whose value is returned.
5987          * @return {String} The current value of the style property for this element.
5988          */
5989         getStyle : function(){
5990             return view && view.getComputedStyle ?
5991                 function(prop){
5992                     var el = this.dom,
5993                         v,
5994                         cs,
5995                         out,
5996                         display,
5997                         wk = Ext.isWebKit,
5998                         display;
5999
6000                     if(el == document){
6001                         return null;
6002                     }
6003                     prop = chkCache(prop);
6004                     // Fix bug caused by this: https://bugs.webkit.org/show_bug.cgi?id=13343
6005                     if(wk && (/marginRight/.test(prop))) {
6006                         display = this.getStyle('display');
6007                         el.style.display = 'inline-block';
6008                     }
6009                     out = (v = el.style[prop]) ? v :
6010                            (cs = view.getComputedStyle(el, "")) ? cs[prop] : null;
6011
6012                     // Webkit returns rgb values for transparent.
6013                     if(wk){
6014                         if(out == 'rgba(0, 0, 0, 0)'){
6015                             out = 'transparent';
6016                         }else if(display){
6017                             el.style.display = display;
6018                         }
6019                     }
6020                     return out;
6021                 } :
6022                 function(prop){
6023                     var el = this.dom,
6024                         m,
6025                         cs;
6026
6027                     if(el == document) return null;
6028                     if (prop == 'opacity') {
6029                         if (el.style.filter.match) {
6030                             if(m = el.style.filter.match(opacityRe)){
6031                                 var fv = parseFloat(m[1]);
6032                                 if(!isNaN(fv)){
6033                                     return fv ? fv / 100 : 0;
6034                                 }
6035                             }
6036                         }
6037                         return 1;
6038                     }
6039                     prop = chkCache(prop);
6040                     return el.style[prop] || ((cs = el.currentStyle) ? cs[prop] : null);
6041                 };
6042         }(),
6043
6044         /**
6045          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
6046          * are convert to standard 6 digit hex color.
6047          * @param {String} attr The css attribute
6048          * @param {String} defaultValue The default value to use when a valid color isn't found
6049          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
6050          * color anims.
6051          */
6052         getColor : function(attr, defaultValue, prefix){
6053             var v = this.getStyle(attr),
6054                 color = (typeof prefix != 'undefined') ? prefix : '#',
6055                 h;
6056
6057             if(!v || (/transparent|inherit/.test(v))) {
6058                 return defaultValue;
6059             }
6060             if(/^r/.test(v)){
6061                 Ext.each(v.slice(4, v.length -1).split(','), function(s){
6062                     h = parseInt(s, 10);
6063                     color += (h < 16 ? '0' : '') + h.toString(16);
6064                 });
6065             }else{
6066                 v = v.replace('#', '');
6067                 color += v.length == 3 ? v.replace(/^(\w)(\w)(\w)$/, '$1$1$2$2$3$3') : v;
6068             }
6069             return(color.length > 5 ? color.toLowerCase() : defaultValue);
6070         },
6071
6072         /**
6073          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
6074          * @param {String/Object} property The style property to be set, or an object of multiple styles.
6075          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
6076          * @return {Ext.Element} this
6077          */
6078         setStyle : function(prop, value){
6079             var tmp, style;
6080             
6081             if (typeof prop != 'object') {
6082                 tmp = {};
6083                 tmp[prop] = value;
6084                 prop = tmp;
6085             }
6086             for (style in prop) {
6087                 value = prop[style];
6088                 style == 'opacity' ?
6089                     this.setOpacity(value) :
6090                     this.dom.style[chkCache(style)] = value;
6091             }
6092             return this;
6093         },
6094
6095         /**
6096          * Set the opacity of the element
6097          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
6098          * @param {Boolean/Object} animate (optional) a standard Element animation config object or <tt>true</tt> for
6099          * the default animation (<tt>{duration: .35, easing: 'easeIn'}</tt>)
6100          * @return {Ext.Element} this
6101          */
6102          setOpacity : function(opacity, animate){
6103             var me = this,
6104                 s = me.dom.style;
6105
6106             if(!animate || !me.anim){
6107                 if(Ext.isIE){
6108                     var opac = opacity < 1 ? 'alpha(opacity=' + opacity * 100 + ')' : '',
6109                     val = s.filter.replace(opacityRe, '').replace(trimRe, '');
6110
6111                     s.zoom = 1;
6112                     s.filter = val + (val.length > 0 ? ' ' : '') + opac;
6113                 }else{
6114                     s.opacity = opacity;
6115                 }
6116             }else{
6117                 me.anim({opacity: {to: opacity}}, me.preanim(arguments, 1), null, .35, 'easeIn');
6118             }
6119             return me;
6120         },
6121
6122         /**
6123          * Clears any opacity settings from this element. Required in some cases for IE.
6124          * @return {Ext.Element} this
6125          */
6126         clearOpacity : function(){
6127             var style = this.dom.style;
6128             if(Ext.isIE){
6129                 if(!Ext.isEmpty(style.filter)){
6130                     style.filter = style.filter.replace(opacityRe, '').replace(trimRe, '');
6131                 }
6132             }else{
6133                 style.opacity = style['-moz-opacity'] = style['-khtml-opacity'] = '';
6134             }
6135             return this;
6136         },
6137
6138         /**
6139          * Returns the offset height of the element
6140          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
6141          * @return {Number} The element's height
6142          */
6143         getHeight : function(contentHeight){
6144             var me = this,
6145                 dom = me.dom,
6146                 hidden = Ext.isIE && me.isStyle('display', 'none'),
6147                 h = MATH.max(dom.offsetHeight, hidden ? 0 : dom.clientHeight) || 0;
6148
6149             h = !contentHeight ? h : h - me.getBorderWidth("tb") - me.getPadding("tb");
6150             return h < 0 ? 0 : h;
6151         },
6152
6153         /**
6154          * Returns the offset width of the element
6155          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
6156          * @return {Number} The element's width
6157          */
6158         getWidth : function(contentWidth){
6159             var me = this,
6160                 dom = me.dom,
6161                 hidden = Ext.isIE && me.isStyle('display', 'none'),
6162                 w = MATH.max(dom.offsetWidth, hidden ? 0 : dom.clientWidth) || 0;
6163             w = !contentWidth ? w : w - me.getBorderWidth("lr") - me.getPadding("lr");
6164             return w < 0 ? 0 : w;
6165         },
6166
6167         /**
6168          * Set the width of this Element.
6169          * @param {Mixed} width The new width. This may be one of:<div class="mdetail-params"><ul>
6170          * <li>A Number specifying the new width in this Element's {@link #defaultUnit}s (by default, pixels).</li>
6171          * <li>A String used to set the CSS width style. Animation may <b>not</b> be used.
6172          * </ul></div>
6173          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
6174          * @return {Ext.Element} this
6175          */
6176         setWidth : function(width, animate){
6177             var me = this;
6178             width = me.adjustWidth(width);
6179             !animate || !me.anim ?
6180                 me.dom.style.width = me.addUnits(width) :
6181                 me.anim({width : {to : width}}, me.preanim(arguments, 1));
6182             return me;
6183         },
6184
6185         /**
6186          * Set the height of this Element.
6187          * <pre><code>
6188 // change the height to 200px and animate with default configuration
6189 Ext.fly('elementId').setHeight(200, true);
6190
6191 // change the height to 150px and animate with a custom configuration
6192 Ext.fly('elId').setHeight(150, {
6193     duration : .5, // animation will have a duration of .5 seconds
6194     // will change the content to "finished"
6195     callback: function(){ this.{@link #update}("finished"); }
6196 });
6197          * </code></pre>
6198          * @param {Mixed} height The new height. This may be one of:<div class="mdetail-params"><ul>
6199          * <li>A Number specifying the new height in this Element's {@link #defaultUnit}s (by default, pixels.)</li>
6200          * <li>A String used to set the CSS height style. Animation may <b>not</b> be used.</li>
6201          * </ul></div>
6202          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
6203          * @return {Ext.Element} this
6204          */
6205          setHeight : function(height, animate){
6206             var me = this;
6207             height = me.adjustHeight(height);
6208             !animate || !me.anim ?
6209                 me.dom.style.height = me.addUnits(height) :
6210                 me.anim({height : {to : height}}, me.preanim(arguments, 1));
6211             return me;
6212         },
6213
6214         /**
6215          * Gets the width of the border(s) for the specified side(s)
6216          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
6217          * passing <tt>'lr'</tt> would get the border <b><u>l</u></b>eft width + the border <b><u>r</u></b>ight width.
6218          * @return {Number} The width of the sides passed added together
6219          */
6220         getBorderWidth : function(side){
6221             return this.addStyles(side, borders);
6222         },
6223
6224         /**
6225          * Gets the width of the padding(s) for the specified side(s)
6226          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
6227          * passing <tt>'lr'</tt> would get the padding <b><u>l</u></b>eft + the padding <b><u>r</u></b>ight.
6228          * @return {Number} The padding of the sides passed added together
6229          */
6230         getPadding : function(side){
6231             return this.addStyles(side, paddings);
6232         },
6233
6234         /**
6235          *  Store the current overflow setting and clip overflow on the element - use <tt>{@link #unclip}</tt> to remove
6236          * @return {Ext.Element} this
6237          */
6238         clip : function(){
6239             var me = this,
6240                 dom = me.dom;
6241
6242             if(!data(dom, ISCLIPPED)){
6243                 data(dom, ISCLIPPED, true);
6244                 data(dom, ORIGINALCLIP, {
6245                     o: me.getStyle(OVERFLOW),
6246                     x: me.getStyle(OVERFLOWX),
6247                     y: me.getStyle(OVERFLOWY)
6248                 });
6249                 me.setStyle(OVERFLOW, HIDDEN);
6250                 me.setStyle(OVERFLOWX, HIDDEN);
6251                 me.setStyle(OVERFLOWY, HIDDEN);
6252             }
6253             return me;
6254         },
6255
6256         /**
6257          *  Return clipping (overflow) to original clipping before <tt>{@link #clip}</tt> was called
6258          * @return {Ext.Element} this
6259          */
6260         unclip : function(){
6261             var me = this,
6262                 dom = me.dom;
6263
6264             if(data(dom, ISCLIPPED)){
6265                 data(dom, ISCLIPPED, false);
6266                 var o = data(dom, ORIGINALCLIP);
6267                 if(o.o){
6268                     me.setStyle(OVERFLOW, o.o);
6269                 }
6270                 if(o.x){
6271                     me.setStyle(OVERFLOWX, o.x);
6272                 }
6273                 if(o.y){
6274                     me.setStyle(OVERFLOWY, o.y);
6275                 }
6276             }
6277             return me;
6278         },
6279
6280         // private
6281         addStyles : function(sides, styles){
6282             var ttlSize = 0,
6283                 sidesArr = sides.match(wordsRe),
6284                 side,
6285                 size,
6286                 i,
6287                 len = sidesArr.length;
6288             for (i = 0; i < len; i++) {
6289                 side = sidesArr[i];
6290                 size = side && parseInt(this.getStyle(styles[side]), 10);
6291                 if (size) {
6292                     ttlSize += MATH.abs(size);
6293                 }
6294             }
6295             return ttlSize;
6296         },
6297
6298         margins : margins
6299     };
6300 }()
6301 );
6302 /**
6303  * @class Ext.Element
6304  */
6305
6306 // special markup used throughout Ext when box wrapping elements
6307 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>';
6308
6309 Ext.Element.addMethods(function(){
6310     var INTERNAL = "_internal",
6311         pxMatch = /(\d+\.?\d+)px/;
6312     return {
6313         /**
6314          * More flexible version of {@link #setStyle} for setting style properties.
6315          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
6316          * a function which returns such a specification.
6317          * @return {Ext.Element} this
6318          */
6319         applyStyles : function(style){
6320             Ext.DomHelper.applyStyles(this.dom, style);
6321             return this;
6322         },
6323
6324         /**
6325          * Returns an object with properties matching the styles requested.
6326          * For example, el.getStyles('color', 'font-size', 'width') might return
6327          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
6328          * @param {String} style1 A style name
6329          * @param {String} style2 A style name
6330          * @param {String} etc.
6331          * @return {Object} The style object
6332          */
6333         getStyles : function(){
6334             var ret = {};
6335             Ext.each(arguments, function(v) {
6336                ret[v] = this.getStyle(v);
6337             },
6338             this);
6339             return ret;
6340         },
6341
6342         // private  ==> used by ext full
6343         setOverflow : function(v){
6344             var dom = this.dom;
6345             if(v=='auto' && Ext.isMac && Ext.isGecko2){ // work around stupid FF 2.0/Mac scroll bar bug
6346                 dom.style.overflow = 'hidden';
6347                 (function(){dom.style.overflow = 'auto';}).defer(1);
6348             }else{
6349                 dom.style.overflow = v;
6350             }
6351         },
6352
6353        /**
6354         * <p>Wraps the specified element with a special 9 element markup/CSS block that renders by default as
6355         * a gray container with a gradient background, rounded corners and a 4-way shadow.</p>
6356         * <p>This special markup is used throughout Ext when box wrapping elements ({@link Ext.Button},
6357         * {@link Ext.Panel} when <tt>{@link Ext.Panel#frame frame=true}</tt>, {@link Ext.Window}).  The markup
6358         * is of this form:</p>
6359         * <pre><code>
6360     Ext.Element.boxMarkup =
6361     &#39;&lt;div class="{0}-tl">&lt;div class="{0}-tr">&lt;div class="{0}-tc">&lt;/div>&lt;/div>&lt;/div>
6362      &lt;div class="{0}-ml">&lt;div class="{0}-mr">&lt;div class="{0}-mc">&lt;/div>&lt;/div>&lt;/div>
6363      &lt;div class="{0}-bl">&lt;div class="{0}-br">&lt;div class="{0}-bc">&lt;/div>&lt;/div>&lt;/div>&#39;;
6364         * </code></pre>
6365         * <p>Example usage:</p>
6366         * <pre><code>
6367     // Basic box wrap
6368     Ext.get("foo").boxWrap();
6369
6370     // You can also add a custom class and use CSS inheritance rules to customize the box look.
6371     // 'x-box-blue' is a built-in alternative -- look at the related CSS definitions as an example
6372     // for how to create a custom box wrap style.
6373     Ext.get("foo").boxWrap().addClass("x-box-blue");
6374         * </code></pre>
6375         * @param {String} class (optional) A base CSS class to apply to the containing wrapper element
6376         * (defaults to <tt>'x-box'</tt>). Note that there are a number of CSS rules that are dependent on
6377         * this name to make the overall effect work, so if you supply an alternate base class, make sure you
6378         * also supply all of the necessary rules.
6379         * @return {Ext.Element} The outermost wrapping element of the created box structure.
6380         */
6381         boxWrap : function(cls){
6382             cls = cls || 'x-box';
6383             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)));
6384             Ext.DomQuery.selectNode('.' + cls + '-mc', el.dom).appendChild(this.dom);
6385             return el;
6386         },
6387
6388         /**
6389          * Set the size of this Element. If animation is true, both width and height will be animated concurrently.
6390          * @param {Mixed} width The new width. This may be one of:<div class="mdetail-params"><ul>
6391          * <li>A Number specifying the new width in this Element's {@link #defaultUnit}s (by default, pixels).</li>
6392          * <li>A String used to set the CSS width style. Animation may <b>not</b> be used.
6393          * <li>A size object in the format <code>{width: widthValue, height: heightValue}</code>.</li>
6394          * </ul></div>
6395          * @param {Mixed} height The new height. This may be one of:<div class="mdetail-params"><ul>
6396          * <li>A Number specifying the new height in this Element's {@link #defaultUnit}s (by default, pixels).</li>
6397          * <li>A String used to set the CSS height style. Animation may <b>not</b> be used.</li>
6398          * </ul></div>
6399          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
6400          * @return {Ext.Element} this
6401          */
6402         setSize : function(width, height, animate){
6403             var me = this;
6404             if(typeof width == 'object'){ // in case of object from getSize()
6405                 height = width.height;
6406                 width = width.width;
6407             }
6408             width = me.adjustWidth(width);
6409             height = me.adjustHeight(height);
6410             if(!animate || !me.anim){
6411                 me.dom.style.width = me.addUnits(width);
6412                 me.dom.style.height = me.addUnits(height);
6413             }else{
6414                 me.anim({width: {to: width}, height: {to: height}}, me.preanim(arguments, 2));
6415             }
6416             return me;
6417         },
6418
6419         /**
6420          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
6421          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
6422          * if a height has not been set using CSS.
6423          * @return {Number}
6424          */
6425         getComputedHeight : function(){
6426             var me = this,
6427                 h = Math.max(me.dom.offsetHeight, me.dom.clientHeight);
6428             if(!h){
6429                 h = parseFloat(me.getStyle('height')) || 0;
6430                 if(!me.isBorderBox()){
6431                     h += me.getFrameWidth('tb');
6432                 }
6433             }
6434             return h;
6435         },
6436
6437         /**
6438          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
6439          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
6440          * if a width has not been set using CSS.
6441          * @return {Number}
6442          */
6443         getComputedWidth : function(){
6444             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
6445             if(!w){
6446                 w = parseFloat(this.getStyle('width')) || 0;
6447                 if(!this.isBorderBox()){
6448                     w += this.getFrameWidth('lr');
6449                 }
6450             }
6451             return w;
6452         },
6453
6454         /**
6455          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
6456          for more information about the sides.
6457          * @param {String} sides
6458          * @return {Number}
6459          */
6460         getFrameWidth : function(sides, onlyContentBox){
6461             return onlyContentBox && this.isBorderBox() ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
6462         },
6463
6464         /**
6465          * Sets up event handlers to add and remove a css class when the mouse is over this element
6466          * @param {String} className
6467          * @return {Ext.Element} this
6468          */
6469         addClassOnOver : function(className){
6470             this.hover(
6471                 function(){
6472                     Ext.fly(this, INTERNAL).addClass(className);
6473                 },
6474                 function(){
6475                     Ext.fly(this, INTERNAL).removeClass(className);
6476                 }
6477             );
6478             return this;
6479         },
6480
6481         /**
6482          * Sets up event handlers to add and remove a css class when this element has the focus
6483          * @param {String} className
6484          * @return {Ext.Element} this
6485          */
6486         addClassOnFocus : function(className){
6487             this.on("focus", function(){
6488                 Ext.fly(this, INTERNAL).addClass(className);
6489             }, this.dom);
6490             this.on("blur", function(){
6491                 Ext.fly(this, INTERNAL).removeClass(className);
6492             }, this.dom);
6493             return this;
6494         },
6495
6496         /**
6497          * 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)
6498          * @param {String} className
6499          * @return {Ext.Element} this
6500          */
6501         addClassOnClick : function(className){
6502             var dom = this.dom;
6503             this.on("mousedown", function(){
6504                 Ext.fly(dom, INTERNAL).addClass(className);
6505                 var d = Ext.getDoc(),
6506                     fn = function(){
6507                         Ext.fly(dom, INTERNAL).removeClass(className);
6508                         d.removeListener("mouseup", fn);
6509                     };
6510                 d.on("mouseup", fn);
6511             });
6512             return this;
6513         },
6514
6515         /**
6516          * <p>Returns the dimensions of the element available to lay content out in.<p>
6517          * <p>If the element (or any ancestor element) has CSS style <code>display : none</code>, the dimensions will be zero.</p>
6518          * example:<pre><code>
6519         var vpSize = Ext.getBody().getViewSize();
6520
6521         // all Windows created afterwards will have a default value of 90% height and 95% width
6522         Ext.Window.override({
6523             width: vpSize.width * 0.9,
6524             height: vpSize.height * 0.95
6525         });
6526         // To handle window resizing you would have to hook onto onWindowResize.
6527         * </code></pre>
6528         *
6529         * getViewSize utilizes clientHeight/clientWidth which excludes sizing of scrollbars.
6530         * To obtain the size including scrollbars, use getStyleSize
6531         *
6532         * Sizing of the document body is handled at the adapter level which handles special cases for IE and strict modes, etc.
6533         */
6534
6535         getViewSize : function(){
6536             var doc = document,
6537                 d = this.dom,
6538                 isDoc = (d == doc || d == doc.body);
6539
6540             // If the body, use Ext.lib.Dom
6541             if (isDoc) {
6542                 var extdom = Ext.lib.Dom;
6543                 return {
6544                     width : extdom.getViewWidth(),
6545                     height : extdom.getViewHeight()
6546                 };
6547
6548             // Else use clientHeight/clientWidth
6549             } else {
6550                 return {
6551                     width : d.clientWidth,
6552                     height : d.clientHeight
6553                 }
6554             }
6555         },
6556
6557         /**
6558         * <p>Returns the dimensions of the element available to lay content out in.<p>
6559         *
6560         * getStyleSize utilizes prefers style sizing if present, otherwise it chooses the larger of offsetHeight/clientHeight and offsetWidth/clientWidth.
6561         * To obtain the size excluding scrollbars, use getViewSize
6562         *
6563         * Sizing of the document body is handled at the adapter level which handles special cases for IE and strict modes, etc.
6564         */
6565
6566         getStyleSize : function(){
6567             var me = this,
6568                 w, h,
6569                 doc = document,
6570                 d = this.dom,
6571                 isDoc = (d == doc || d == doc.body),
6572                 s = d.style;
6573
6574             // If the body, use Ext.lib.Dom
6575             if (isDoc) {
6576                 var extdom = Ext.lib.Dom;
6577                 return {
6578                     width : extdom.getViewWidth(),
6579                     height : extdom.getViewHeight()
6580                 }
6581             }
6582             // Use Styles if they are set
6583             if(s.width && s.width != 'auto'){
6584                 w = parseFloat(s.width);
6585                 if(me.isBorderBox()){
6586                    w -= me.getFrameWidth('lr');
6587                 }
6588             }
6589             // Use Styles if they are set
6590             if(s.height && s.height != 'auto'){
6591                 h = parseFloat(s.height);
6592                 if(me.isBorderBox()){
6593                    h -= me.getFrameWidth('tb');
6594                 }
6595             }
6596             // Use getWidth/getHeight if style not set.
6597             return {width: w || me.getWidth(true), height: h || me.getHeight(true)};
6598         },
6599
6600         /**
6601          * Returns the size of the element.
6602          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
6603          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
6604          */
6605         getSize : function(contentSize){
6606             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
6607         },
6608
6609         /**
6610          * Forces the browser to repaint this element
6611          * @return {Ext.Element} this
6612          */
6613         repaint : function(){
6614             var dom = this.dom;
6615             this.addClass("x-repaint");
6616             setTimeout(function(){
6617                 Ext.fly(dom).removeClass("x-repaint");
6618             }, 1);
6619             return this;
6620         },
6621
6622         /**
6623          * Disables text selection for this element (normalized across browsers)
6624          * @return {Ext.Element} this
6625          */
6626         unselectable : function(){
6627             this.dom.unselectable = "on";
6628             return this.swallowEvent("selectstart", true).
6629                         applyStyles("-moz-user-select:none;-khtml-user-select:none;").
6630                         addClass("x-unselectable");
6631         },
6632
6633         /**
6634          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
6635          * then it returns the calculated width of the sides (see getPadding)
6636          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
6637          * @return {Object/Number}
6638          */
6639         getMargins : function(side){
6640             var me = this,
6641                 key,
6642                 hash = {t:"top", l:"left", r:"right", b: "bottom"},
6643                 o = {};
6644
6645             if (!side) {
6646                 for (key in me.margins){
6647                     o[hash[key]] = parseFloat(me.getStyle(me.margins[key])) || 0;
6648                 }
6649                 return o;
6650             } else {
6651                 return me.addStyles.call(me, side, me.margins);
6652             }
6653         }
6654     };
6655 }());
6656 /**
6657  * @class Ext.Element
6658  */
6659 (function(){
6660 var D = Ext.lib.Dom,
6661         LEFT = "left",
6662         RIGHT = "right",
6663         TOP = "top",
6664         BOTTOM = "bottom",
6665         POSITION = "position",
6666         STATIC = "static",
6667         RELATIVE = "relative",
6668         AUTO = "auto",
6669         ZINDEX = "z-index";
6670
6671 Ext.Element.addMethods({
6672         /**
6673       * 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).
6674       * @return {Number} The X position of the element
6675       */
6676     getX : function(){
6677         return D.getX(this.dom);
6678     },
6679
6680     /**
6681       * 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).
6682       * @return {Number} The Y position of the element
6683       */
6684     getY : function(){
6685         return D.getY(this.dom);
6686     },
6687
6688     /**
6689       * 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).
6690       * @return {Array} The XY position of the element
6691       */
6692     getXY : function(){
6693         return D.getXY(this.dom);
6694     },
6695
6696     /**
6697       * 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.
6698       * @param {Mixed} element The element to get the offsets from.
6699       * @return {Array} The XY page offsets (e.g. [100, -200])
6700       */
6701     getOffsetsTo : function(el){
6702         var o = this.getXY(),
6703                 e = Ext.fly(el, '_internal').getXY();
6704         return [o[0]-e[0],o[1]-e[1]];
6705     },
6706
6707     /**
6708      * 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).
6709      * @param {Number} The X position of the element
6710      * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
6711      * @return {Ext.Element} this
6712      */
6713     setX : function(x, animate){            
6714             return this.setXY([x, this.getY()], this.animTest(arguments, animate, 1));
6715     },
6716
6717     /**
6718      * 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).
6719      * @param {Number} The Y position of the element
6720      * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
6721      * @return {Ext.Element} this
6722      */
6723     setY : function(y, animate){            
6724             return this.setXY([this.getX(), y], this.animTest(arguments, animate, 1));
6725     },
6726
6727     /**
6728      * Sets the element's left position directly using CSS style (instead of {@link #setX}).
6729      * @param {String} left The left CSS property value
6730      * @return {Ext.Element} this
6731      */
6732     setLeft : function(left){
6733         this.setStyle(LEFT, this.addUnits(left));
6734         return this;
6735     },
6736
6737     /**
6738      * Sets the element's top position directly using CSS style (instead of {@link #setY}).
6739      * @param {String} top The top CSS property value
6740      * @return {Ext.Element} this
6741      */
6742     setTop : function(top){
6743         this.setStyle(TOP, this.addUnits(top));
6744         return this;
6745     },
6746
6747     /**
6748      * Sets the element's CSS right style.
6749      * @param {String} right The right CSS property value
6750      * @return {Ext.Element} this
6751      */
6752     setRight : function(right){
6753         this.setStyle(RIGHT, this.addUnits(right));
6754         return this;
6755     },
6756
6757     /**
6758      * Sets the element's CSS bottom style.
6759      * @param {String} bottom The bottom CSS property value
6760      * @return {Ext.Element} this
6761      */
6762     setBottom : function(bottom){
6763         this.setStyle(BOTTOM, this.addUnits(bottom));
6764         return this;
6765     },
6766
6767     /**
6768      * Sets the position of the element in page coordinates, regardless of how the element is positioned.
6769      * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
6770      * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
6771      * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
6772      * @return {Ext.Element} this
6773      */
6774     setXY : function(pos, animate){
6775             var me = this;
6776         if(!animate || !me.anim){
6777             D.setXY(me.dom, pos);
6778         }else{
6779             me.anim({points: {to: pos}}, me.preanim(arguments, 1), 'motion');
6780         }
6781         return me;
6782     },
6783
6784     /**
6785      * Sets the position of the element in page coordinates, regardless of how the element is positioned.
6786      * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
6787      * @param {Number} x X value for new position (coordinates are page-based)
6788      * @param {Number} y Y value for new position (coordinates are page-based)
6789      * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
6790      * @return {Ext.Element} this
6791      */
6792     setLocation : function(x, y, animate){
6793         return this.setXY([x, y], this.animTest(arguments, animate, 2));
6794     },
6795
6796     /**
6797      * Sets the position of the element in page coordinates, regardless of how the element is positioned.
6798      * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
6799      * @param {Number} x X value for new position (coordinates are page-based)
6800      * @param {Number} y Y value for new position (coordinates are page-based)
6801      * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
6802      * @return {Ext.Element} this
6803      */
6804     moveTo : function(x, y, animate){
6805         return this.setXY([x, y], this.animTest(arguments, animate, 2));        
6806     },    
6807     
6808     /**
6809      * Gets the left X coordinate
6810      * @param {Boolean} local True to get the local css position instead of page coordinate
6811      * @return {Number}
6812      */
6813     getLeft : function(local){
6814             return !local ? this.getX() : parseInt(this.getStyle(LEFT), 10) || 0;
6815     },
6816
6817     /**
6818      * Gets the right X coordinate of the element (element X position + element width)
6819      * @param {Boolean} local True to get the local css position instead of page coordinate
6820      * @return {Number}
6821      */
6822     getRight : function(local){
6823             var me = this;
6824             return !local ? me.getX() + me.getWidth() : (me.getLeft(true) + me.getWidth()) || 0;
6825     },
6826
6827     /**
6828      * Gets the top Y coordinate
6829      * @param {Boolean} local True to get the local css position instead of page coordinate
6830      * @return {Number}
6831      */
6832     getTop : function(local) {
6833             return !local ? this.getY() : parseInt(this.getStyle(TOP), 10) || 0;
6834     },
6835
6836     /**
6837      * Gets the bottom Y coordinate of the element (element Y position + element height)
6838      * @param {Boolean} local True to get the local css position instead of page coordinate
6839      * @return {Number}
6840      */
6841     getBottom : function(local){
6842             var me = this;
6843             return !local ? me.getY() + me.getHeight() : (me.getTop(true) + me.getHeight()) || 0;
6844     },
6845
6846     /**
6847     * Initializes positioning on this element. If a desired position is not passed, it will make the
6848     * the element positioned relative IF it is not already positioned.
6849     * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
6850     * @param {Number} zIndex (optional) The zIndex to apply
6851     * @param {Number} x (optional) Set the page X position
6852     * @param {Number} y (optional) Set the page Y position
6853     */
6854     position : function(pos, zIndex, x, y){
6855             var me = this;
6856             
6857         if(!pos && me.isStyle(POSITION, STATIC)){           
6858             me.setStyle(POSITION, RELATIVE);           
6859         } else if(pos) {
6860             me.setStyle(POSITION, pos);
6861         }
6862         if(zIndex){
6863             me.setStyle(ZINDEX, zIndex);
6864         }
6865         if(x || y) me.setXY([x || false, y || false]);
6866     },
6867
6868     /**
6869     * Clear positioning back to the default when the document was loaded
6870     * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
6871     * @return {Ext.Element} this
6872      */
6873     clearPositioning : function(value){
6874         value = value || '';
6875         this.setStyle({
6876             left : value,
6877             right : value,
6878             top : value,
6879             bottom : value,
6880             "z-index" : "",
6881             position : STATIC
6882         });
6883         return this;
6884     },
6885
6886     /**
6887     * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
6888     * snapshot before performing an update and then restoring the element.
6889     * @return {Object}
6890     */
6891     getPositioning : function(){
6892         var l = this.getStyle(LEFT);
6893         var t = this.getStyle(TOP);
6894         return {
6895             "position" : this.getStyle(POSITION),
6896             "left" : l,
6897             "right" : l ? "" : this.getStyle(RIGHT),
6898             "top" : t,
6899             "bottom" : t ? "" : this.getStyle(BOTTOM),
6900             "z-index" : this.getStyle(ZINDEX)
6901         };
6902     },
6903     
6904     /**
6905     * Set positioning with an object returned by getPositioning().
6906     * @param {Object} posCfg
6907     * @return {Ext.Element} this
6908      */
6909     setPositioning : function(pc){
6910             var me = this,
6911                 style = me.dom.style;
6912                 
6913         me.setStyle(pc);
6914         
6915         if(pc.right == AUTO){
6916             style.right = "";
6917         }
6918         if(pc.bottom == AUTO){
6919             style.bottom = "";
6920         }
6921         
6922         return me;
6923     },    
6924         
6925     /**
6926      * Translates the passed page coordinates into left/top css values for this element
6927      * @param {Number/Array} x The page x or an array containing [x, y]
6928      * @param {Number} y (optional) The page y, required if x is not an array
6929      * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
6930      */
6931     translatePoints : function(x, y){                
6932             y = isNaN(x[1]) ? y : x[1];
6933         x = isNaN(x[0]) ? x : x[0];
6934         var me = this,
6935                 relative = me.isStyle(POSITION, RELATIVE),
6936                 o = me.getXY(),
6937                 l = parseInt(me.getStyle(LEFT), 10),
6938                 t = parseInt(me.getStyle(TOP), 10);
6939         
6940         l = !isNaN(l) ? l : (relative ? 0 : me.dom.offsetLeft);
6941         t = !isNaN(t) ? t : (relative ? 0 : me.dom.offsetTop);        
6942
6943         return {left: (x - o[0] + l), top: (y - o[1] + t)}; 
6944     },
6945     
6946     animTest : function(args, animate, i) {
6947         return !!animate && this.preanim ? this.preanim(args, i) : false;
6948     }
6949 });
6950 })();/**
6951  * @class Ext.Element
6952  */
6953 Ext.Element.addMethods({
6954     /**
6955      * 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.
6956      * @param {Object} box The box to fill {x, y, width, height}
6957      * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
6958      * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
6959      * @return {Ext.Element} this
6960      */
6961     setBox : function(box, adjust, animate){
6962         var me = this,
6963                 w = box.width, 
6964                 h = box.height;
6965         if((adjust && !me.autoBoxAdjust) && !me.isBorderBox()){
6966            w -= (me.getBorderWidth("lr") + me.getPadding("lr"));
6967            h -= (me.getBorderWidth("tb") + me.getPadding("tb"));
6968         }
6969         me.setBounds(box.x, box.y, w, h, me.animTest.call(me, arguments, animate, 2));
6970         return me;
6971     },
6972
6973     /**
6974      * Return an object defining the area of this Element which can be passed to {@link #setBox} to
6975      * set another Element's size/location to match this element.
6976      * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
6977      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
6978      * @return {Object} box An object in the format<pre><code>
6979 {
6980     x: &lt;Element's X position>,
6981     y: &lt;Element's Y position>,
6982     width: &lt;Element's width>,
6983     height: &lt;Element's height>,
6984     bottom: &lt;Element's lower bound>,
6985     right: &lt;Element's rightmost bound>
6986 }
6987 </code></pre>
6988      * The returned object may also be addressed as an Array where index 0 contains the X position
6989      * and index 1 contains the Y position. So the result may also be used for {@link #setXY}
6990      */
6991         getBox : function(contentBox, local) {      
6992             var me = this,
6993                 xy,
6994                 left,
6995                 top,
6996                 getBorderWidth = me.getBorderWidth,
6997                 getPadding = me.getPadding, 
6998                 l,
6999                 r,
7000                 t,
7001                 b;
7002         if(!local){
7003             xy = me.getXY();
7004         }else{
7005             left = parseInt(me.getStyle("left"), 10) || 0;
7006             top = parseInt(me.getStyle("top"), 10) || 0;
7007             xy = [left, top];
7008         }
7009         var el = me.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
7010         if(!contentBox){
7011             bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
7012         }else{
7013             l = getBorderWidth.call(me, "l") + getPadding.call(me, "l");
7014             r = getBorderWidth.call(me, "r") + getPadding.call(me, "r");
7015             t = getBorderWidth.call(me, "t") + getPadding.call(me, "t");
7016             b = getBorderWidth.call(me, "b") + getPadding.call(me, "b");
7017             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)};
7018         }
7019         bx.right = bx.x + bx.width;
7020         bx.bottom = bx.y + bx.height;
7021         return bx;
7022         },
7023         
7024     /**
7025      * Move this element relative to its current position.
7026      * @param {String} direction Possible values are: "l" (or "left"), "r" (or "right"), "t" (or "top", or "up"), "b" (or "bottom", or "down").
7027      * @param {Number} distance How far to move the element in pixels
7028      * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7029      * @return {Ext.Element} this
7030      */
7031      move : function(direction, distance, animate){
7032         var me = this,          
7033                 xy = me.getXY(),
7034                 x = xy[0],
7035                 y = xy[1],              
7036                 left = [x - distance, y],
7037                 right = [x + distance, y],
7038                 top = [x, y - distance],
7039                 bottom = [x, y + distance],
7040                 hash = {
7041                         l :     left,
7042                         left : left,
7043                         r : right,
7044                         right : right,
7045                         t : top,
7046                         top : top,
7047                         up : top,
7048                         b : bottom, 
7049                         bottom : bottom,
7050                         down : bottom                           
7051                 };
7052         
7053             direction = direction.toLowerCase();    
7054             me.moveTo(hash[direction][0], hash[direction][1], me.animTest.call(me, arguments, animate, 2));
7055     },
7056     
7057     /**
7058      * Quick set left and top adding default units
7059      * @param {String} left The left CSS property value
7060      * @param {String} top The top CSS property value
7061      * @return {Ext.Element} this
7062      */
7063      setLeftTop : function(left, top){
7064             var me = this,
7065                 style = me.dom.style;
7066         style.left = me.addUnits(left);
7067         style.top = me.addUnits(top);
7068         return me;
7069     },
7070     
7071     /**
7072      * Returns the region of the given element.
7073      * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7074      * @return {Region} A Ext.lib.Region containing "top, left, bottom, right" member data.
7075      */
7076     getRegion : function(){
7077         return Ext.lib.Dom.getRegion(this.dom);
7078     },
7079     
7080     /**
7081      * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
7082      * @param {Number} x X value for new position (coordinates are page-based)
7083      * @param {Number} y Y value for new position (coordinates are page-based)
7084      * @param {Mixed} width The new width. This may be one of:<div class="mdetail-params"><ul>
7085      * <li>A Number specifying the new width in this Element's {@link #defaultUnit}s (by default, pixels)</li>
7086      * <li>A String used to set the CSS width style. Animation may <b>not</b> be used.
7087      * </ul></div>
7088      * @param {Mixed} height The new height. This may be one of:<div class="mdetail-params"><ul>
7089      * <li>A Number specifying the new height in this Element's {@link #defaultUnit}s (by default, pixels)</li>
7090      * <li>A String used to set the CSS height style. Animation may <b>not</b> be used.</li>
7091      * </ul></div>
7092      * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7093      * @return {Ext.Element} this
7094      */
7095     setBounds : function(x, y, width, height, animate){
7096             var me = this;
7097         if (!animate || !me.anim) {
7098             me.setSize(width, height);
7099             me.setLocation(x, y);
7100         } else {
7101             me.anim({points: {to: [x, y]}, 
7102                          width: {to: me.adjustWidth(width)}, 
7103                          height: {to: me.adjustHeight(height)}},
7104                      me.preanim(arguments, 4), 
7105                      'motion');
7106         }
7107         return me;
7108     },
7109
7110     /**
7111      * Sets the element's position and size the specified region. If animation is true then width, height, x and y will be animated concurrently.
7112      * @param {Ext.lib.Region} region The region to fill
7113      * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7114      * @return {Ext.Element} this
7115      */
7116     setRegion : function(region, animate) {
7117         return this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.animTest.call(this, arguments, animate, 1));
7118     }
7119 });/**
7120  * @class Ext.Element
7121  */
7122 Ext.Element.addMethods({
7123     /**
7124      * Returns true if this element is scrollable.
7125      * @return {Boolean}
7126      */
7127     isScrollable : function(){
7128         var dom = this.dom;
7129         return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
7130     },
7131
7132     /**
7133      * 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().
7134      * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
7135      * @param {Number} value The new scroll value.
7136      * @return {Element} this
7137      */
7138     scrollTo : function(side, value){
7139         this.dom["scroll" + (/top/i.test(side) ? "Top" : "Left")] = value;
7140         return this;
7141     },
7142
7143     /**
7144      * Returns the current scroll position of the element.
7145      * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
7146      */
7147     getScroll : function(){
7148         var d = this.dom, 
7149             doc = document,
7150             body = doc.body,
7151             docElement = doc.documentElement,
7152             l,
7153             t,
7154             ret;
7155
7156         if(d == doc || d == body){
7157             if(Ext.isIE && Ext.isStrict){
7158                 l = docElement.scrollLeft; 
7159                 t = docElement.scrollTop;
7160             }else{
7161                 l = window.pageXOffset;
7162                 t = window.pageYOffset;
7163             }
7164             ret = {left: l || (body ? body.scrollLeft : 0), top: t || (body ? body.scrollTop : 0)};
7165         }else{
7166             ret = {left: d.scrollLeft, top: d.scrollTop};
7167         }
7168         return ret;
7169     }
7170 });/**
7171  * @class Ext.Element
7172  */
7173 Ext.Element.addMethods({
7174     /**
7175      * 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().
7176      * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
7177      * @param {Number} value The new scroll value
7178      * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7179      * @return {Element} this
7180      */
7181     scrollTo : function(side, value, animate) {
7182         //check if we're scrolling top or left
7183         var top = /top/i.test(side),
7184             me = this,
7185             dom = me.dom,
7186             prop;
7187         if (!animate || !me.anim) {
7188             // just setting the value, so grab the direction
7189             prop = 'scroll' + (top ? 'Top' : 'Left');
7190             dom[prop] = value;
7191         }
7192         else {
7193             // if scrolling top, we need to grab scrollLeft, if left, scrollTop
7194             prop = 'scroll' + (top ? 'Left' : 'Top');
7195             me.anim({scroll: {to: top ? [dom[prop], value] : [value, dom[prop]]}}, me.preanim(arguments, 2), 'scroll');
7196         }
7197         return me;
7198     },
7199     
7200     /**
7201      * Scrolls this element into view within the passed container.
7202      * @param {Mixed} container (optional) The container element to scroll (defaults to document.body).  Should be a
7203      * string (id), dom node, or Ext.Element.
7204      * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7205      * @return {Ext.Element} this
7206      */
7207     scrollIntoView : function(container, hscroll) {
7208         var c = Ext.getDom(container) || Ext.getBody().dom,
7209             el = this.dom,
7210             o = this.getOffsetsTo(c),
7211             l = o[0] + c.scrollLeft,
7212             t = o[1] + c.scrollTop,
7213             b = t + el.offsetHeight,
7214             r = l + el.offsetWidth,
7215             ch = c.clientHeight,
7216             ct = parseInt(c.scrollTop, 10),
7217             cl = parseInt(c.scrollLeft, 10),
7218             cb = ct + ch,
7219             cr = cl + c.clientWidth;
7220
7221         if (el.offsetHeight > ch || t < ct) {
7222             c.scrollTop = t;
7223         }
7224         else if (b > cb) {
7225             c.scrollTop = b-ch;
7226         }
7227         // corrects IE, other browsers will ignore
7228         c.scrollTop = c.scrollTop;
7229
7230         if (hscroll !== false) {
7231             if (el.offsetWidth > c.clientWidth || l < cl) {
7232                 c.scrollLeft = l;
7233             }
7234             else if (r > cr) {
7235                 c.scrollLeft = r - c.clientWidth;
7236             }
7237             c.scrollLeft = c.scrollLeft;
7238         }
7239         return this;
7240     },
7241
7242     // private
7243     scrollChildIntoView : function(child, hscroll) {
7244         Ext.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7245     },
7246     
7247     /**
7248      * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
7249      * within this element's scrollable range.
7250      * @param {String} direction Possible values are: "l" (or "left"), "r" (or "right"), "t" (or "top", or "up"), "b" (or "bottom", or "down").
7251      * @param {Number} distance How far to scroll the element in pixels
7252      * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7253      * @return {Boolean} Returns true if a scroll was triggered or false if the element
7254      * was scrolled as far as it could go.
7255      */
7256      scroll : function(direction, distance, animate) {
7257         if (!this.isScrollable()) {
7258             return false;
7259         }
7260         var el = this.dom,
7261             l = el.scrollLeft, t = el.scrollTop,
7262             w = el.scrollWidth, h = el.scrollHeight,
7263             cw = el.clientWidth, ch = el.clientHeight,
7264             scrolled = false, v,
7265             hash = {
7266                 l: Math.min(l + distance, w-cw),
7267                 r: v = Math.max(l - distance, 0),
7268                 t: Math.max(t - distance, 0),
7269                 b: Math.min(t + distance, h-ch)
7270             };
7271             hash.d = hash.b;
7272             hash.u = hash.t;
7273         
7274         direction = direction.substr(0, 1);
7275         if ((v = hash[direction]) > -1) {
7276             scrolled = true;
7277             this.scrollTo(direction == 'l' || direction == 'r' ? 'left' : 'top', v, this.preanim(arguments, 2));
7278         }
7279         return scrolled;
7280     }
7281 });/**
7282  * @class Ext.Element
7283  */
7284 /**
7285  * Visibility mode constant for use with {@link #setVisibilityMode}. Use visibility to hide element
7286  * @static
7287  * @type Number
7288  */
7289 Ext.Element.VISIBILITY = 1;
7290 /**
7291  * Visibility mode constant for use with {@link #setVisibilityMode}. Use display to hide element
7292  * @static
7293  * @type Number
7294  */
7295 Ext.Element.DISPLAY = 2;
7296
7297 Ext.Element.addMethods(function(){
7298     var VISIBILITY = "visibility",
7299         DISPLAY = "display",
7300         HIDDEN = "hidden",
7301         OFFSETS = "offsets",
7302         NONE = "none",
7303         ORIGINALDISPLAY = 'originalDisplay',
7304         VISMODE = 'visibilityMode',
7305         ELDISPLAY = Ext.Element.DISPLAY,
7306         data = Ext.Element.data,
7307         getDisplay = function(dom){
7308             var d = data(dom, ORIGINALDISPLAY);
7309             if(d === undefined){
7310                 data(dom, ORIGINALDISPLAY, d = '');
7311             }
7312             return d;
7313         },
7314         getVisMode = function(dom){
7315             var m = data(dom, VISMODE);
7316             if(m === undefined){
7317                 data(dom, VISMODE, m = 1);
7318             }
7319             return m;
7320         };
7321
7322     return {
7323         /**
7324          * The element's default display mode  (defaults to "")
7325          * @type String
7326          */
7327         originalDisplay : "",
7328         visibilityMode : 1,
7329
7330         /**
7331          * Sets the element's visibility mode. When setVisible() is called it
7332          * will use this to determine whether to set the visibility or the display property.
7333          * @param {Number} visMode Ext.Element.VISIBILITY or Ext.Element.DISPLAY
7334          * @return {Ext.Element} this
7335          */
7336         setVisibilityMode : function(visMode){
7337             data(this.dom, VISMODE, visMode);
7338             return this;
7339         },
7340
7341         /**
7342          * Perform custom animation on this element.
7343          * <div><ul class="mdetail-params">
7344          * <li><u>Animation Properties</u></li>
7345          *
7346          * <p>The Animation Control Object enables gradual transitions for any member of an
7347          * element's style object that takes a numeric value including but not limited to
7348          * these properties:</p><div><ul class="mdetail-params">
7349          * <li><tt>bottom, top, left, right</tt></li>
7350          * <li><tt>height, width</tt></li>
7351          * <li><tt>margin, padding</tt></li>
7352          * <li><tt>borderWidth</tt></li>
7353          * <li><tt>opacity</tt></li>
7354          * <li><tt>fontSize</tt></li>
7355          * <li><tt>lineHeight</tt></li>
7356          * </ul></div>
7357          *
7358          *
7359          * <li><u>Animation Property Attributes</u></li>
7360          *
7361          * <p>Each Animation Property is a config object with optional properties:</p>
7362          * <div><ul class="mdetail-params">
7363          * <li><tt>by</tt>*  : relative change - start at current value, change by this value</li>
7364          * <li><tt>from</tt> : ignore current value, start from this value</li>
7365          * <li><tt>to</tt>*  : start at current value, go to this value</li>
7366          * <li><tt>unit</tt> : any allowable unit specification</li>
7367          * <p>* do not specify both <tt>to</tt> and <tt>by</tt> for an animation property</p>
7368          * </ul></div>
7369          *
7370          * <li><u>Animation Types</u></li>
7371          *
7372          * <p>The supported animation types:</p><div><ul class="mdetail-params">
7373          * <li><tt>'run'</tt> : Default
7374          * <pre><code>
7375 var el = Ext.get('complexEl');
7376 el.animate(
7377     // animation control object
7378     {
7379         borderWidth: {to: 3, from: 0},
7380         opacity: {to: .3, from: 1},
7381         height: {to: 50, from: el.getHeight()},
7382         width: {to: 300, from: el.getWidth()},
7383         top  : {by: - 100, unit: 'px'},
7384     },
7385     0.35,      // animation duration
7386     null,      // callback
7387     'easeOut', // easing method
7388     'run'      // animation type ('run','color','motion','scroll')
7389 );
7390          * </code></pre>
7391          * </li>
7392          * <li><tt>'color'</tt>
7393          * <p>Animates transition of background, text, or border colors.</p>
7394          * <pre><code>
7395 el.animate(
7396     // animation control object
7397     {
7398         color: { to: '#06e' },
7399         backgroundColor: { to: '#e06' }
7400     },
7401     0.35,      // animation duration
7402     null,      // callback
7403     'easeOut', // easing method
7404     'color'    // animation type ('run','color','motion','scroll')
7405 );
7406          * </code></pre>
7407          * </li>
7408          *
7409          * <li><tt>'motion'</tt>
7410          * <p>Animates the motion of an element to/from specific points using optional bezier
7411          * way points during transit.</p>
7412          * <pre><code>
7413 el.animate(
7414     // animation control object
7415     {
7416         borderWidth: {to: 3, from: 0},
7417         opacity: {to: .3, from: 1},
7418         height: {to: 50, from: el.getHeight()},
7419         width: {to: 300, from: el.getWidth()},
7420         top  : {by: - 100, unit: 'px'},
7421         points: {
7422             to: [50, 100],  // go to this point
7423             control: [      // optional bezier way points
7424                 [ 600, 800],
7425                 [-100, 200]
7426             ]
7427         }
7428     },
7429     3000,      // animation duration (milliseconds!)
7430     null,      // callback
7431     'easeOut', // easing method
7432     'motion'   // animation type ('run','color','motion','scroll')
7433 );
7434          * </code></pre>
7435          * </li>
7436          * <li><tt>'scroll'</tt>
7437          * <p>Animate horizontal or vertical scrolling of an overflowing page element.</p>
7438          * <pre><code>
7439 el.animate(
7440     // animation control object
7441     {
7442         scroll: {to: [400, 300]}
7443     },
7444     0.35,      // animation duration
7445     null,      // callback
7446     'easeOut', // easing method
7447     'scroll'   // animation type ('run','color','motion','scroll')
7448 );
7449          * </code></pre>
7450          * </li>
7451          * </ul></div>
7452          *
7453          * </ul></div>
7454          *
7455          * @param {Object} args The animation control args
7456          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to <tt>.35</tt>)
7457          * @param {Function} onComplete (optional) Function to call when animation completes
7458          * @param {String} easing (optional) {@link Ext.Fx#easing} method to use (defaults to <tt>'easeOut'</tt>)
7459          * @param {String} animType (optional) <tt>'run'</tt> is the default. Can also be <tt>'color'</tt>,
7460          * <tt>'motion'</tt>, or <tt>'scroll'</tt>
7461          * @return {Ext.Element} this
7462          */
7463         animate : function(args, duration, onComplete, easing, animType){
7464             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7465             return this;
7466         },
7467
7468         /*
7469          * @private Internal animation call
7470          */
7471         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7472             animType = animType || 'run';
7473             opt = opt || {};
7474             var me = this,
7475                 anim = Ext.lib.Anim[animType](
7476                     me.dom,
7477                     args,
7478                     (opt.duration || defaultDur) || .35,
7479                     (opt.easing || defaultEase) || 'easeOut',
7480                     function(){
7481                         if(cb) cb.call(me);
7482                         if(opt.callback) opt.callback.call(opt.scope || me, me, opt);
7483                     },
7484                     me
7485                 );
7486             opt.anim = anim;
7487             return anim;
7488         },
7489
7490         // private legacy anim prep
7491         preanim : function(a, i){
7492             return !a[i] ? false : (typeof a[i] == 'object' ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7493         },
7494
7495         /**
7496          * Checks whether the element is currently visible using both visibility and display properties.
7497          * @return {Boolean} True if the element is currently visible, else false
7498          */
7499         isVisible : function() {
7500             return !this.isStyle(VISIBILITY, HIDDEN) && !this.isStyle(DISPLAY, NONE);
7501         },
7502
7503         /**
7504          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7505          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7506          * @param {Boolean} visible Whether the element is visible
7507          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7508          * @return {Ext.Element} this
7509          */
7510          setVisible : function(visible, animate){
7511             var me = this, isDisplay, isVisible, isOffsets,
7512                 dom = me.dom;
7513
7514             // hideMode string override
7515             if (typeof animate == 'string'){
7516                 isDisplay = animate == DISPLAY;
7517                 isVisible = animate == VISIBILITY;
7518                 isOffsets = animate == OFFSETS;
7519                 animate = false;
7520             } else {
7521                 isDisplay = getVisMode(this.dom) == ELDISPLAY;
7522                 isVisible = !isDisplay;
7523             }
7524
7525             if (!animate || !me.anim) {
7526                 if (isDisplay){
7527                     me.setDisplayed(visible);
7528                 } else if (isOffsets){
7529                     if (!visible){
7530                         me.hideModeStyles = {
7531                             position: me.getStyle('position'),
7532                             top: me.getStyle('top'),
7533                             left: me.getStyle('left')
7534                         };
7535
7536                         me.applyStyles({position: 'absolute', top: '-10000px', left: '-10000px'});
7537                     } else {
7538                         me.applyStyles(me.hideModeStyles || {position: '', top: '', left: ''});
7539                     }
7540                 }else{
7541                     me.fixDisplay();
7542                     dom.style.visibility = visible ? "visible" : HIDDEN;
7543                 }
7544             }else{
7545                 // closure for composites
7546                 if (visible){
7547                     me.setOpacity(.01);
7548                     me.setVisible(true);
7549                 }
7550                 me.anim({opacity: { to: (visible?1:0) }},
7551                         me.preanim(arguments, 1),
7552                         null,
7553                         .35,
7554                         'easeIn',
7555                         function(){
7556                              if(!visible){
7557                                  dom.style[isDisplay ? DISPLAY : VISIBILITY] = (isDisplay) ? NONE : HIDDEN;
7558                                  Ext.fly(dom).setOpacity(1);
7559                              }
7560                         });
7561             }
7562             return me;
7563         },
7564
7565         /**
7566          * Toggles the element's visibility or display, depending on visibility mode.
7567          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7568          * @return {Ext.Element} this
7569          */
7570         toggle : function(animate){
7571             var me = this;
7572             me.setVisible(!me.isVisible(), me.preanim(arguments, 0));
7573             return me;
7574         },
7575
7576         /**
7577          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7578          * @param {Mixed} value Boolean value to display the element using its default display, or a string to set the display directly.
7579          * @return {Ext.Element} this
7580          */
7581         setDisplayed : function(value) {
7582             if(typeof value == "boolean"){
7583                value = value ? getDisplay(this.dom) : NONE;
7584             }
7585             this.setStyle(DISPLAY, value);
7586             return this;
7587         },
7588
7589         // private
7590         fixDisplay : function(){
7591             var me = this;
7592             if(me.isStyle(DISPLAY, NONE)){
7593                 me.setStyle(VISIBILITY, HIDDEN);
7594                 me.setStyle(DISPLAY, getDisplay(this.dom)); // first try reverting to default
7595                 if(me.isStyle(DISPLAY, NONE)){ // if that fails, default to block
7596                     me.setStyle(DISPLAY, "block");
7597                 }
7598             }
7599         },
7600
7601         /**
7602          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
7603          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7604          * @return {Ext.Element} this
7605          */
7606         hide : function(animate){
7607             // hideMode override
7608             if (typeof animate == 'string'){
7609                 this.setVisible(false, animate);
7610                 return this;
7611             }
7612             this.setVisible(false, this.preanim(arguments, 0));
7613             return this;
7614         },
7615
7616         /**
7617         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
7618         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7619          * @return {Ext.Element} this
7620          */
7621         show : function(animate){
7622             // hideMode override
7623             if (typeof animate == 'string'){
7624                 this.setVisible(true, animate);
7625                 return this;
7626             }
7627             this.setVisible(true, this.preanim(arguments, 0));
7628             return this;
7629         }
7630     };
7631 }());
7632 /**
7633  * @class Ext.Element
7634  */
7635 Ext.Element.addMethods(
7636 function(){
7637     var VISIBILITY = "visibility",
7638         DISPLAY = "display",
7639         HIDDEN = "hidden",
7640         NONE = "none",
7641             XMASKED = "x-masked",
7642                 XMASKEDRELATIVE = "x-masked-relative",
7643         data = Ext.Element.data;
7644
7645         return {
7646                 /**
7647              * Checks whether the element is currently visible using both visibility and display properties.
7648              * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7649              * @return {Boolean} True if the element is currently visible, else false
7650              */
7651             isVisible : function(deep) {
7652                 var vis = !this.isStyle(VISIBILITY,HIDDEN) && !this.isStyle(DISPLAY,NONE),
7653                         p = this.dom.parentNode;
7654                 if(deep !== true || !vis){
7655                     return vis;
7656                 }
7657                 while(p && !/^body/i.test(p.tagName)){
7658                     if(!Ext.fly(p, '_isVisible').isVisible()){
7659                         return false;
7660                     }
7661                     p = p.parentNode;
7662                 }
7663                 return true;
7664             },
7665
7666             /**
7667              * Returns true if display is not "none"
7668              * @return {Boolean}
7669              */
7670             isDisplayed : function() {
7671                 return !this.isStyle(DISPLAY, NONE);
7672             },
7673
7674                 /**
7675              * Convenience method for setVisibilityMode(Element.DISPLAY)
7676              * @param {String} display (optional) What to set display to when visible
7677              * @return {Ext.Element} this
7678              */
7679             enableDisplayMode : function(display){
7680                 this.setVisibilityMode(Ext.Element.DISPLAY);
7681                 if(!Ext.isEmpty(display)){
7682                 data(this.dom, 'originalDisplay', display);
7683             }
7684                 return this;
7685             },
7686
7687                 /**
7688              * Puts a mask over this element to disable user interaction. Requires core.css.
7689              * This method can only be applied to elements which accept child nodes.
7690              * @param {String} msg (optional) A message to display in the mask
7691              * @param {String} msgCls (optional) A css class to apply to the msg element
7692              * @return {Element} The mask element
7693              */
7694             mask : function(msg, msgCls){
7695                     var me = this,
7696                         dom = me.dom,
7697                         dh = Ext.DomHelper,
7698                         EXTELMASKMSG = "ext-el-mask-msg",
7699                 el,
7700                 mask;
7701
7702                 if(!/^body/i.test(dom.tagName) && me.getStyle('position') == 'static'){
7703                     me.addClass(XMASKEDRELATIVE);
7704                 }
7705                 if((el = data(dom, 'maskMsg'))){
7706                     el.remove();
7707                 }
7708                 if((el = data(dom, 'mask'))){
7709                     el.remove();
7710                 }
7711
7712             mask = dh.append(dom, {cls : "ext-el-mask"}, true);
7713                 data(dom, 'mask', mask);
7714
7715                 me.addClass(XMASKED);
7716                 mask.setDisplayed(true);
7717                 if(typeof msg == 'string'){
7718                 var mm = dh.append(dom, {cls : EXTELMASKMSG, cn:{tag:'div'}}, true);
7719                 data(dom, 'maskMsg', mm);
7720                     mm.dom.className = msgCls ? EXTELMASKMSG + " " + msgCls : EXTELMASKMSG;
7721                     mm.dom.firstChild.innerHTML = msg;
7722                     mm.setDisplayed(true);
7723                     mm.center(me);
7724                 }
7725                 if(Ext.isIE && !(Ext.isIE7 && Ext.isStrict) && me.getStyle('height') == 'auto'){ // ie will not expand full height automatically
7726                     mask.setSize(undefined, me.getHeight());
7727                 }
7728                 return mask;
7729             },
7730
7731             /**
7732              * Removes a previously applied mask.
7733              */
7734             unmask : function(){
7735                     var me = this,
7736                 dom = me.dom,
7737                         mask = data(dom, 'mask'),
7738                         maskMsg = data(dom, 'maskMsg');
7739                 if(mask){
7740                     if(maskMsg){
7741                         maskMsg.remove();
7742                     data(dom, 'maskMsg', undefined);
7743                     }
7744                     mask.remove();
7745                 data(dom, 'mask', undefined);
7746                 }
7747                 if(me.isMasked()){
7748                 me.removeClass([XMASKED, XMASKEDRELATIVE]);
7749             }
7750             },
7751
7752             /**
7753              * Returns true if this element is masked
7754              * @return {Boolean}
7755              */
7756             isMasked : function(){
7757             var m = data(this.dom, 'mask');
7758                 return m && m.isVisible();
7759             },
7760
7761             /**
7762              * Creates an iframe shim for this element to keep selects and other windowed objects from
7763              * showing through.
7764              * @return {Ext.Element} The new shim element
7765              */
7766             createShim : function(){
7767                 var el = document.createElement('iframe'),
7768                         shim;
7769                 el.frameBorder = '0';
7770                 el.className = 'ext-shim';
7771                 el.src = Ext.SSL_SECURE_URL;
7772                 shim = Ext.get(this.dom.parentNode.insertBefore(el, this.dom));
7773                 shim.autoBoxAdjust = false;
7774                 return shim;
7775             }
7776     };
7777 }());/**
7778  * @class Ext.Element
7779  */
7780 Ext.Element.addMethods({
7781     /**
7782      * Convenience method for constructing a KeyMap
7783      * @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:
7784      * <code>{key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}</code>
7785      * @param {Function} fn The function to call
7786      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the specified function is executed. Defaults to this Element.
7787      * @return {Ext.KeyMap} The KeyMap created
7788      */
7789     addKeyListener : function(key, fn, scope){
7790         var config;
7791         if(typeof key != 'object' || Ext.isArray(key)){
7792             config = {
7793                 key: key,
7794                 fn: fn,
7795                 scope: scope
7796             };
7797         }else{
7798             config = {
7799                 key : key.key,
7800                 shift : key.shift,
7801                 ctrl : key.ctrl,
7802                 alt : key.alt,
7803                 fn: fn,
7804                 scope: scope
7805             };
7806         }
7807         return new Ext.KeyMap(this, config);
7808     },
7809
7810     /**
7811      * Creates a KeyMap for this element
7812      * @param {Object} config The KeyMap config. See {@link Ext.KeyMap} for more details
7813      * @return {Ext.KeyMap} The KeyMap created
7814      */
7815     addKeyMap : function(config){
7816         return new Ext.KeyMap(this, config);
7817     }
7818 });
7819 (function(){
7820     // contants
7821     var NULL = null,
7822         UNDEFINED = undefined,
7823         TRUE = true,
7824         FALSE = false,
7825         SETX = "setX",
7826         SETY = "setY",
7827         SETXY = "setXY",
7828         LEFT = "left",
7829         BOTTOM = "bottom",
7830         TOP = "top",
7831         RIGHT = "right",
7832         HEIGHT = "height",
7833         WIDTH = "width",
7834         POINTS = "points",
7835         HIDDEN = "hidden",
7836         ABSOLUTE = "absolute",
7837         VISIBLE = "visible",
7838         MOTION = "motion",
7839         POSITION = "position",
7840         EASEOUT = "easeOut",
7841         /*
7842          * Use a light flyweight here since we are using so many callbacks and are always assured a DOM element
7843          */
7844         flyEl = new Ext.Element.Flyweight(),
7845         queues = {},
7846         getObject = function(o){
7847             return o || {};
7848         },
7849         fly = function(dom){
7850             flyEl.dom = dom;
7851             flyEl.id = Ext.id(dom);
7852             return flyEl;
7853         },
7854         /*
7855          * Queueing now stored outside of the element due to closure issues
7856          */
7857         getQueue = function(id){
7858             if(!queues[id]){
7859                 queues[id] = [];
7860             }
7861             return queues[id];
7862         },
7863         setQueue = function(id, value){
7864             queues[id] = value;
7865         };
7866         
7867 //Notifies Element that fx methods are available
7868 Ext.enableFx = TRUE;
7869
7870 /**
7871  * @class Ext.Fx
7872  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
7873  * to the {@link Ext.Element} interface when included, so all effects calls should be performed via {@link Ext.Element}.
7874  * Conversely, since the effects are not actually defined in {@link Ext.Element}, Ext.Fx <b>must</b> be
7875  * {@link Ext#enableFx included} in order for the Element effects to work.</p><br/>
7876  * 
7877  * <p><b><u>Method Chaining</u></b></p>
7878  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
7879  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
7880  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
7881  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
7882  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
7883  * expected results and should be done with care.  Also see <tt>{@link #callback}</tt>.</p><br/>
7884  *
7885  * <p><b><u>Anchor Options for Motion Effects</u></b></p>
7886  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
7887  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
7888 <pre>
7889 Value  Description
7890 -----  -----------------------------
7891 tl     The top left corner
7892 t      The center of the top edge
7893 tr     The top right corner
7894 l      The center of the left edge
7895 r      The center of the right edge
7896 bl     The bottom left corner
7897 b      The center of the bottom edge
7898 br     The bottom right corner
7899 </pre>
7900  * <b>Note</b>: some Fx methods accept specific custom config parameters.  The options shown in the Config Options
7901  * section below are common options that can be passed to any Fx method unless otherwise noted.</b>
7902  * 
7903  * @cfg {Function} callback A function called when the effect is finished.  Note that effects are queued internally by the
7904  * Fx class, so a callback is not required to specify another effect -- effects can simply be chained together
7905  * and called in sequence (see note for <b><u>Method Chaining</u></b> above), for example:<pre><code>
7906  * el.slideIn().highlight();
7907  * </code></pre>
7908  * The callback is intended for any additional code that should run once a particular effect has completed. The Element
7909  * being operated upon is passed as the first parameter.
7910  * 
7911  * @cfg {Object} scope The scope (<code>this</code> reference) in which the <tt>{@link #callback}</tt> function is executed. Defaults to the browser window.
7912  * 
7913  * @cfg {String} easing A valid Ext.lib.Easing value for the effect:</p><div class="mdetail-params"><ul>
7914  * <li><b><tt>backBoth</tt></b></li>
7915  * <li><b><tt>backIn</tt></b></li>
7916  * <li><b><tt>backOut</tt></b></li>
7917  * <li><b><tt>bounceBoth</tt></b></li>
7918  * <li><b><tt>bounceIn</tt></b></li>
7919  * <li><b><tt>bounceOut</tt></b></li>
7920  * <li><b><tt>easeBoth</tt></b></li>
7921  * <li><b><tt>easeBothStrong</tt></b></li>
7922  * <li><b><tt>easeIn</tt></b></li>
7923  * <li><b><tt>easeInStrong</tt></b></li>
7924  * <li><b><tt>easeNone</tt></b></li>
7925  * <li><b><tt>easeOut</tt></b></li>
7926  * <li><b><tt>easeOutStrong</tt></b></li>
7927  * <li><b><tt>elasticBoth</tt></b></li>
7928  * <li><b><tt>elasticIn</tt></b></li>
7929  * <li><b><tt>elasticOut</tt></b></li>
7930  * </ul></div>
7931  *
7932  * @cfg {String} afterCls A css class to apply after the effect
7933  * @cfg {Number} duration The length of time (in seconds) that the effect should last
7934  * 
7935  * @cfg {Number} endOpacity Only applicable for {@link #fadeIn} or {@link #fadeOut}, a number between
7936  * <tt>0</tt> and <tt>1</tt> inclusive to configure the ending opacity value.
7937  *  
7938  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
7939  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
7940  * effects that end with the element being visually hidden, ignored otherwise)
7941  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. <tt>"width:100px"</tt>, or an object
7942  * in the form <tt>{width:"100px"}</tt>, or a function which returns such a specification that will be applied to the
7943  * Element after the effect finishes.
7944  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
7945  * @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
7946  * @cfg {Boolean} stopFx Whether preceding effects should be stopped and removed before running current effect (only applies to non blocking effects)
7947  */
7948 Ext.Fx = {
7949     
7950     // private - calls the function taking arguments from the argHash based on the key.  Returns the return value of the function.
7951     //           this is useful for replacing switch statements (for example).
7952     switchStatements : function(key, fn, argHash){
7953         return fn.apply(this, argHash[key]);
7954     },
7955     
7956     /**
7957      * Slides the element into view.  An anchor point can be optionally passed to set the point of
7958      * origin for the slide effect.  This function automatically handles wrapping the element with
7959      * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
7960      * Usage:
7961      *<pre><code>
7962 // default: slide the element in from the top
7963 el.slideIn();
7964
7965 // custom: slide the element in from the right with a 2-second duration
7966 el.slideIn('r', { duration: 2 });
7967
7968 // common config options shown with default values
7969 el.slideIn('t', {
7970     easing: 'easeOut',
7971     duration: .5
7972 });
7973 </code></pre>
7974      * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
7975      * @param {Object} options (optional) Object literal with any of the Fx config options
7976      * @return {Ext.Element} The Element
7977      */
7978     slideIn : function(anchor, o){ 
7979         o = getObject(o);
7980         var me = this,
7981             dom = me.dom,
7982             st = dom.style,
7983             xy,
7984             r,
7985             b,              
7986             wrap,               
7987             after,
7988             st,
7989             args, 
7990             pt,
7991             bw,
7992             bh;
7993             
7994         anchor = anchor || "t";
7995
7996         me.queueFx(o, function(){            
7997             xy = fly(dom).getXY();
7998             // fix display to visibility
7999             fly(dom).fixDisplay();            
8000             
8001             // restore values after effect
8002             r = fly(dom).getFxRestore();      
8003             b = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: dom.offsetWidth, height: dom.offsetHeight};
8004             b.right = b.x + b.width;
8005             b.bottom = b.y + b.height;
8006             
8007             // fixed size for slide
8008             fly(dom).setWidth(b.width).setHeight(b.height);            
8009             
8010             // wrap if needed
8011             wrap = fly(dom).fxWrap(r.pos, o, HIDDEN);
8012             
8013             st.visibility = VISIBLE;
8014             st.position = ABSOLUTE;
8015             
8016             // clear out temp styles after slide and unwrap
8017             function after(){
8018                  fly(dom).fxUnwrap(wrap, r.pos, o);
8019                  st.width = r.width;
8020                  st.height = r.height;
8021                  fly(dom).afterFx(o);
8022             }
8023             
8024             // time to calculate the positions        
8025             pt = {to: [b.x, b.y]}; 
8026             bw = {to: b.width};
8027             bh = {to: b.height};
8028                 
8029             function argCalc(wrap, style, ww, wh, sXY, sXYval, s1, s2, w, h, p){                    
8030                 var ret = {};
8031                 fly(wrap).setWidth(ww).setHeight(wh);
8032                 if(fly(wrap)[sXY]){
8033                     fly(wrap)[sXY](sXYval);                  
8034                 }
8035                 style[s1] = style[s2] = "0";                    
8036                 if(w){
8037                     ret.width = w
8038                 };
8039                 if(h){
8040                     ret.height = h;
8041                 }
8042                 if(p){
8043                     ret.points = p;
8044                 }
8045                 return ret;
8046             };
8047
8048             args = fly(dom).switchStatements(anchor.toLowerCase(), argCalc, {
8049                     t  : [wrap, st, b.width, 0, NULL, NULL, LEFT, BOTTOM, NULL, bh, NULL],
8050                     l  : [wrap, st, 0, b.height, NULL, NULL, RIGHT, TOP, bw, NULL, NULL],
8051                     r  : [wrap, st, b.width, b.height, SETX, b.right, LEFT, TOP, NULL, NULL, pt],
8052                     b  : [wrap, st, b.width, b.height, SETY, b.bottom, LEFT, TOP, NULL, bh, pt],
8053                     tl : [wrap, st, 0, 0, NULL, NULL, RIGHT, BOTTOM, bw, bh, pt],
8054                     bl : [wrap, st, 0, 0, SETY, b.y + b.height, RIGHT, TOP, bw, bh, pt],
8055                     br : [wrap, st, 0, 0, SETXY, [b.right, b.bottom], LEFT, TOP, bw, bh, pt],
8056                     tr : [wrap, st, 0, 0, SETX, b.x + b.width, LEFT, BOTTOM, bw, bh, pt]
8057                 });
8058             
8059             st.visibility = VISIBLE;
8060             fly(wrap).show();
8061
8062             arguments.callee.anim = fly(wrap).fxanim(args,
8063                 o,
8064                 MOTION,
8065                 .5,
8066                 EASEOUT, 
8067                 after);
8068         });
8069         return me;
8070     },
8071     
8072     /**
8073      * Slides the element out of view.  An anchor point can be optionally passed to set the end point
8074      * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
8075      * 'hidden') but block elements will still take up space in the document.  The element must be removed
8076      * from the DOM using the 'remove' config option if desired.  This function automatically handles 
8077      * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
8078      * Usage:
8079      *<pre><code>
8080 // default: slide the element out to the top
8081 el.slideOut();
8082
8083 // custom: slide the element out to the right with a 2-second duration
8084 el.slideOut('r', { duration: 2 });
8085
8086 // common config options shown with default values
8087 el.slideOut('t', {
8088     easing: 'easeOut',
8089     duration: .5,
8090     remove: false,
8091     useDisplay: false
8092 });
8093 </code></pre>
8094      * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
8095      * @param {Object} options (optional) Object literal with any of the Fx config options
8096      * @return {Ext.Element} The Element
8097      */
8098     slideOut : function(anchor, o){
8099         o = getObject(o);
8100         var me = this,
8101             dom = me.dom,
8102             st = dom.style,
8103             xy = me.getXY(),
8104             wrap,
8105             r,
8106             b,
8107             a,
8108             zero = {to: 0}; 
8109                     
8110         anchor = anchor || "t";
8111
8112         me.queueFx(o, function(){
8113             
8114             // restore values after effect
8115             r = fly(dom).getFxRestore(); 
8116             b = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: dom.offsetWidth, height: dom.offsetHeight};
8117             b.right = b.x + b.width;
8118             b.bottom = b.y + b.height;
8119                 
8120             // fixed size for slide   
8121             fly(dom).setWidth(b.width).setHeight(b.height);
8122
8123             // wrap if needed
8124             wrap = fly(dom).fxWrap(r.pos, o, VISIBLE);
8125                 
8126             st.visibility = VISIBLE;
8127             st.position = ABSOLUTE;
8128             fly(wrap).setWidth(b.width).setHeight(b.height);            
8129
8130             function after(){
8131                 o.useDisplay ? fly(dom).setDisplayed(FALSE) : fly(dom).hide();                
8132                 fly(dom).fxUnwrap(wrap, r.pos, o);
8133                 st.width = r.width;
8134                 st.height = r.height;
8135                 fly(dom).afterFx(o);
8136             }            
8137             
8138             function argCalc(style, s1, s2, p1, v1, p2, v2, p3, v3){                    
8139                 var ret = {};
8140                 
8141                 style[s1] = style[s2] = "0";
8142                 ret[p1] = v1;               
8143                 if(p2){
8144                     ret[p2] = v2;               
8145                 }
8146                 if(p3){
8147                     ret[p3] = v3;
8148                 }
8149                 
8150                 return ret;
8151             };
8152             
8153             a = fly(dom).switchStatements(anchor.toLowerCase(), argCalc, {
8154                 t  : [st, LEFT, BOTTOM, HEIGHT, zero],
8155                 l  : [st, RIGHT, TOP, WIDTH, zero],
8156                 r  : [st, LEFT, TOP, WIDTH, zero, POINTS, {to : [b.right, b.y]}],
8157                 b  : [st, LEFT, TOP, HEIGHT, zero, POINTS, {to : [b.x, b.bottom]}],
8158                 tl : [st, RIGHT, BOTTOM, WIDTH, zero, HEIGHT, zero],
8159                 bl : [st, RIGHT, TOP, WIDTH, zero, HEIGHT, zero, POINTS, {to : [b.x, b.bottom]}],
8160                 br : [st, LEFT, TOP, WIDTH, zero, HEIGHT, zero, POINTS, {to : [b.x + b.width, b.bottom]}],
8161                 tr : [st, LEFT, BOTTOM, WIDTH, zero, HEIGHT, zero, POINTS, {to : [b.right, b.y]}]
8162             });
8163             
8164             arguments.callee.anim = fly(wrap).fxanim(a,
8165                 o,
8166                 MOTION,
8167                 .5,
8168                 EASEOUT, 
8169                 after);
8170         });
8171         return me;
8172     },
8173
8174     /**
8175      * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
8176      * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
8177      * The element must be removed from the DOM using the 'remove' config option if desired.
8178      * Usage:
8179      *<pre><code>
8180 // default
8181 el.puff();
8182
8183 // common config options shown with default values
8184 el.puff({
8185     easing: 'easeOut',
8186     duration: .5,
8187     remove: false,
8188     useDisplay: false
8189 });
8190 </code></pre>
8191      * @param {Object} options (optional) Object literal with any of the Fx config options
8192      * @return {Ext.Element} The Element
8193      */
8194     puff : function(o){
8195         o = getObject(o);
8196         var me = this,
8197             dom = me.dom,
8198             st = dom.style,
8199             width,
8200             height,
8201             r;
8202
8203         me.queueFx(o, function(){
8204             width = fly(dom).getWidth();
8205             height = fly(dom).getHeight();
8206             fly(dom).clearOpacity();
8207             fly(dom).show();
8208
8209             // restore values after effect
8210             r = fly(dom).getFxRestore();                   
8211             
8212             function after(){
8213                 o.useDisplay ? fly(dom).setDisplayed(FALSE) : fly(dom).hide();                  
8214                 fly(dom).clearOpacity();  
8215                 fly(dom).setPositioning(r.pos);
8216                 st.width = r.width;
8217                 st.height = r.height;
8218                 st.fontSize = '';
8219                 fly(dom).afterFx(o);
8220             }   
8221
8222             arguments.callee.anim = fly(dom).fxanim({
8223                     width : {to : fly(dom).adjustWidth(width * 2)},
8224                     height : {to : fly(dom).adjustHeight(height * 2)},
8225                     points : {by : [-width * .5, -height * .5]},
8226                     opacity : {to : 0},
8227                     fontSize: {to : 200, unit: "%"}
8228                 },
8229                 o,
8230                 MOTION,
8231                 .5,
8232                 EASEOUT,
8233                  after);
8234         });
8235         return me;
8236     },
8237
8238     /**
8239      * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
8240      * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
8241      * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
8242      * Usage:
8243      *<pre><code>
8244 // default
8245 el.switchOff();
8246
8247 // all config options shown with default values
8248 el.switchOff({
8249     easing: 'easeIn',
8250     duration: .3,
8251     remove: false,
8252     useDisplay: false
8253 });
8254 </code></pre>
8255      * @param {Object} options (optional) Object literal with any of the Fx config options
8256      * @return {Ext.Element} The Element
8257      */
8258     switchOff : function(o){
8259         o = getObject(o);
8260         var me = this,
8261             dom = me.dom,
8262             st = dom.style,
8263             r;
8264
8265         me.queueFx(o, function(){
8266             fly(dom).clearOpacity();
8267             fly(dom).clip();
8268
8269             // restore values after effect
8270             r = fly(dom).getFxRestore();
8271                 
8272             function after(){
8273                 o.useDisplay ? fly(dom).setDisplayed(FALSE) : fly(dom).hide();  
8274                 fly(dom).clearOpacity();
8275                 fly(dom).setPositioning(r.pos);
8276                 st.width = r.width;
8277                 st.height = r.height;   
8278                 fly(dom).afterFx(o);
8279             };
8280
8281             fly(dom).fxanim({opacity : {to : 0.3}}, 
8282                 NULL, 
8283                 NULL, 
8284                 .1, 
8285                 NULL, 
8286                 function(){                                 
8287                     fly(dom).clearOpacity();
8288                         (function(){                            
8289                             fly(dom).fxanim({
8290                                 height : {to : 1},
8291                                 points : {by : [0, fly(dom).getHeight() * .5]}
8292                             }, 
8293                             o, 
8294                             MOTION, 
8295                             0.3, 
8296                             'easeIn', 
8297                             after);
8298                         }).defer(100);
8299                 });
8300         });
8301         return me;
8302     },
8303
8304     /**
8305      * Highlights the Element by setting a color (applies to the background-color by default, but can be
8306      * changed using the "attr" config option) and then fading back to the original color. If no original
8307      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
8308      * Usage:
8309 <pre><code>
8310 // default: highlight background to yellow
8311 el.highlight();
8312
8313 // custom: highlight foreground text to blue for 2 seconds
8314 el.highlight("0000ff", { attr: 'color', duration: 2 });
8315
8316 // common config options shown with default values
8317 el.highlight("ffff9c", {
8318     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
8319     endColor: (current color) or "ffffff",
8320     easing: 'easeIn',
8321     duration: 1
8322 });
8323 </code></pre>
8324      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
8325      * @param {Object} options (optional) Object literal with any of the Fx config options
8326      * @return {Ext.Element} The Element
8327      */ 
8328     highlight : function(color, o){
8329         o = getObject(o);
8330         var me = this,
8331             dom = me.dom,
8332             attr = o.attr || "backgroundColor",
8333             a = {},
8334             restore;
8335
8336         me.queueFx(o, function(){
8337             fly(dom).clearOpacity();
8338             fly(dom).show();
8339
8340             function after(){
8341                 dom.style[attr] = restore;
8342                 fly(dom).afterFx(o);
8343             }            
8344             restore = dom.style[attr];
8345             a[attr] = {from: color || "ffff9c", to: o.endColor || fly(dom).getColor(attr) || "ffffff"};
8346             arguments.callee.anim = fly(dom).fxanim(a,
8347                 o,
8348                 'color',
8349                 1,
8350                 'easeIn', 
8351                 after);
8352         });
8353         return me;
8354     },
8355
8356    /**
8357     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
8358     * Usage:
8359 <pre><code>
8360 // default: a single light blue ripple
8361 el.frame();
8362
8363 // custom: 3 red ripples lasting 3 seconds total
8364 el.frame("ff0000", 3, { duration: 3 });
8365
8366 // common config options shown with default values
8367 el.frame("C3DAF9", 1, {
8368     duration: 1 //duration of each individual ripple.
8369     // Note: Easing is not configurable and will be ignored if included
8370 });
8371 </code></pre>
8372     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
8373     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
8374     * @param {Object} options (optional) Object literal with any of the Fx config options
8375     * @return {Ext.Element} The Element
8376     */
8377     frame : function(color, count, o){
8378         o = getObject(o);
8379         var me = this,
8380             dom = me.dom,
8381             proxy,
8382             active;
8383
8384         me.queueFx(o, function(){
8385             color = color || '#C3DAF9';
8386             if(color.length == 6){
8387                 color = '#' + color;
8388             }            
8389             count = count || 1;
8390             fly(dom).show();
8391
8392             var xy = fly(dom).getXY(),
8393                 b = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: dom.offsetWidth, height: dom.offsetHeight},
8394                 queue = function(){
8395                     proxy = fly(document.body || document.documentElement).createChild({
8396                         style:{
8397                             position : ABSOLUTE,
8398                             'z-index': 35000, // yee haw
8399                             border : '0px solid ' + color
8400                         }
8401                     });
8402                     return proxy.queueFx({}, animFn);
8403                 };
8404             
8405             
8406             arguments.callee.anim = {
8407                 isAnimated: true,
8408                 stop: function() {
8409                     count = 0;
8410                     proxy.stopFx();
8411                 }
8412             };
8413             
8414             function animFn(){
8415                 var scale = Ext.isBorderBox ? 2 : 1;
8416                 active = proxy.anim({
8417                     top : {from : b.y, to : b.y - 20},
8418                     left : {from : b.x, to : b.x - 20},
8419                     borderWidth : {from : 0, to : 10},
8420                     opacity : {from : 1, to : 0},
8421                     height : {from : b.height, to : b.height + 20 * scale},
8422                     width : {from : b.width, to : b.width + 20 * scale}
8423                 },{
8424                     duration: o.duration || 1,
8425                     callback: function() {
8426                         proxy.remove();
8427                         --count > 0 ? queue() : fly(dom).afterFx(o);
8428                     }
8429                 });
8430                 arguments.callee.anim = {
8431                     isAnimated: true,
8432                     stop: function(){
8433                         active.stop();
8434                     }
8435                 };
8436             };
8437             queue();
8438         });
8439         return me;
8440     },
8441
8442    /**
8443     * Creates a pause before any subsequent queued effects begin.  If there are
8444     * no effects queued after the pause it will have no effect.
8445     * Usage:
8446 <pre><code>
8447 el.pause(1);
8448 </code></pre>
8449     * @param {Number} seconds The length of time to pause (in seconds)
8450     * @return {Ext.Element} The Element
8451     */
8452     pause : function(seconds){        
8453         var dom = this.dom,
8454             t;
8455
8456         this.queueFx({}, function(){
8457             t = setTimeout(function(){
8458                 fly(dom).afterFx({});
8459             }, seconds * 1000);
8460             arguments.callee.anim = {
8461                 isAnimated: true,
8462                 stop: function(){
8463                     clearTimeout(t);
8464                     fly(dom).afterFx({});
8465                 }
8466             };
8467         });
8468         return this;
8469     },
8470
8471    /**
8472     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
8473     * using the <tt>{@link #endOpacity}</tt> config option.
8474     * Usage:
8475 <pre><code>
8476 // default: fade in from opacity 0 to 100%
8477 el.fadeIn();
8478
8479 // custom: fade in from opacity 0 to 75% over 2 seconds
8480 el.fadeIn({ endOpacity: .75, duration: 2});
8481
8482 // common config options shown with default values
8483 el.fadeIn({
8484     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
8485     easing: 'easeOut',
8486     duration: .5
8487 });
8488 </code></pre>
8489     * @param {Object} options (optional) Object literal with any of the Fx config options
8490     * @return {Ext.Element} The Element
8491     */
8492     fadeIn : function(o){
8493         o = getObject(o);
8494         var me = this,
8495             dom = me.dom,
8496             to = o.endOpacity || 1;
8497         
8498         me.queueFx(o, function(){
8499             fly(dom).setOpacity(0);
8500             fly(dom).fixDisplay();
8501             dom.style.visibility = VISIBLE;
8502             arguments.callee.anim = fly(dom).fxanim({opacity:{to:to}},
8503                 o, NULL, .5, EASEOUT, function(){
8504                 if(to == 1){
8505                     fly(dom).clearOpacity();
8506                 }
8507                 fly(dom).afterFx(o);
8508             });
8509         });
8510         return me;
8511     },
8512
8513    /**
8514     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
8515     * using the <tt>{@link #endOpacity}</tt> config option.  Note that IE may require
8516     * <tt>{@link #useDisplay}:true</tt> in order to redisplay correctly.
8517     * Usage:
8518 <pre><code>
8519 // default: fade out from the element's current opacity to 0
8520 el.fadeOut();
8521
8522 // custom: fade out from the element's current opacity to 25% over 2 seconds
8523 el.fadeOut({ endOpacity: .25, duration: 2});
8524
8525 // common config options shown with default values
8526 el.fadeOut({
8527     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
8528     easing: 'easeOut',
8529     duration: .5,
8530     remove: false,
8531     useDisplay: false
8532 });
8533 </code></pre>
8534     * @param {Object} options (optional) Object literal with any of the Fx config options
8535     * @return {Ext.Element} The Element
8536     */
8537     fadeOut : function(o){
8538         o = getObject(o);
8539         var me = this,
8540             dom = me.dom,
8541             style = dom.style,
8542             to = o.endOpacity || 0;         
8543         
8544         me.queueFx(o, function(){  
8545             arguments.callee.anim = fly(dom).fxanim({ 
8546                 opacity : {to : to}},
8547                 o, 
8548                 NULL, 
8549                 .5, 
8550                 EASEOUT, 
8551                 function(){
8552                     if(to == 0){
8553                         Ext.Element.data(dom, 'visibilityMode') == Ext.Element.DISPLAY || o.useDisplay ? 
8554                             style.display = "none" :
8555                             style.visibility = HIDDEN;
8556                             
8557                         fly(dom).clearOpacity();
8558                     }
8559                     fly(dom).afterFx(o);
8560             });
8561         });
8562         return me;
8563     },
8564
8565    /**
8566     * Animates the transition of an element's dimensions from a starting height/width
8567     * to an ending height/width.  This method is a convenience implementation of {@link shift}.
8568     * Usage:
8569 <pre><code>
8570 // change height and width to 100x100 pixels
8571 el.scale(100, 100);
8572
8573 // common config options shown with default values.  The height and width will default to
8574 // the element&#39;s existing values if passed as null.
8575 el.scale(
8576     [element&#39;s width],
8577     [element&#39;s height], {
8578         easing: 'easeOut',
8579         duration: .35
8580     }
8581 );
8582 </code></pre>
8583     * @param {Number} width  The new width (pass undefined to keep the original width)
8584     * @param {Number} height  The new height (pass undefined to keep the original height)
8585     * @param {Object} options (optional) Object literal with any of the Fx config options
8586     * @return {Ext.Element} The Element
8587     */
8588     scale : function(w, h, o){
8589         this.shift(Ext.apply({}, o, {
8590             width: w,
8591             height: h
8592         }));
8593         return this;
8594     },
8595
8596    /**
8597     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
8598     * Any of these properties not specified in the config object will not be changed.  This effect 
8599     * requires that at least one new dimension, position or opacity setting must be passed in on
8600     * the config object in order for the function to have any effect.
8601     * Usage:
8602 <pre><code>
8603 // slide the element horizontally to x position 200 while changing the height and opacity
8604 el.shift({ x: 200, height: 50, opacity: .8 });
8605
8606 // common config options shown with default values.
8607 el.shift({
8608     width: [element&#39;s width],
8609     height: [element&#39;s height],
8610     x: [element&#39;s x position],
8611     y: [element&#39;s y position],
8612     opacity: [element&#39;s opacity],
8613     easing: 'easeOut',
8614     duration: .35
8615 });
8616 </code></pre>
8617     * @param {Object} options  Object literal with any of the Fx config options
8618     * @return {Ext.Element} The Element
8619     */
8620     shift : function(o){
8621         o = getObject(o);
8622         var dom = this.dom,
8623             a = {};
8624                 
8625         this.queueFx(o, function(){
8626             for (var prop in o) {
8627                 if (o[prop] != UNDEFINED) {                                                 
8628                     a[prop] = {to : o[prop]};                   
8629                 }
8630             } 
8631             
8632             a.width ? a.width.to = fly(dom).adjustWidth(o.width) : a;
8633             a.height ? a.height.to = fly(dom).adjustWidth(o.height) : a;   
8634             
8635             if (a.x || a.y || a.xy) {
8636                 a.points = a.xy || 
8637                            {to : [ a.x ? a.x.to : fly(dom).getX(),
8638                                    a.y ? a.y.to : fly(dom).getY()]};                  
8639             }
8640
8641             arguments.callee.anim = fly(dom).fxanim(a,
8642                 o, 
8643                 MOTION, 
8644                 .35, 
8645                 EASEOUT, 
8646                 function(){
8647                     fly(dom).afterFx(o);
8648                 });
8649         });
8650         return this;
8651     },
8652
8653     /**
8654      * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
8655      * ending point of the effect.
8656      * Usage:
8657      *<pre><code>
8658 // default: slide the element downward while fading out
8659 el.ghost();
8660
8661 // custom: slide the element out to the right with a 2-second duration
8662 el.ghost('r', { duration: 2 });
8663
8664 // common config options shown with default values
8665 el.ghost('b', {
8666     easing: 'easeOut',
8667     duration: .5,
8668     remove: false,
8669     useDisplay: false
8670 });
8671 </code></pre>
8672      * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
8673      * @param {Object} options (optional) Object literal with any of the Fx config options
8674      * @return {Ext.Element} The Element
8675      */
8676     ghost : function(anchor, o){
8677         o = getObject(o);
8678         var me = this,
8679             dom = me.dom,
8680             st = dom.style,
8681             a = {opacity: {to: 0}, points: {}},
8682             pt = a.points,
8683             r,
8684             w,
8685             h;
8686             
8687         anchor = anchor || "b";
8688
8689         me.queueFx(o, function(){
8690             // restore values after effect
8691             r = fly(dom).getFxRestore();
8692             w = fly(dom).getWidth();
8693             h = fly(dom).getHeight();
8694             
8695             function after(){
8696                 o.useDisplay ? fly(dom).setDisplayed(FALSE) : fly(dom).hide();   
8697                 fly(dom).clearOpacity();
8698                 fly(dom).setPositioning(r.pos);
8699                 st.width = r.width;
8700                 st.height = r.height;
8701                 fly(dom).afterFx(o);
8702             }
8703                 
8704             pt.by = fly(dom).switchStatements(anchor.toLowerCase(), function(v1,v2){ return [v1, v2];}, {
8705                t  : [0, -h],
8706                l  : [-w, 0],
8707                r  : [w, 0],
8708                b  : [0, h],
8709                tl : [-w, -h],
8710                bl : [-w, h],
8711                br : [w, h],
8712                tr : [w, -h] 
8713             });
8714                 
8715             arguments.callee.anim = fly(dom).fxanim(a,
8716                 o,
8717                 MOTION,
8718                 .5,
8719                 EASEOUT, after);
8720         });
8721         return me;
8722     },
8723
8724     /**
8725      * Ensures that all effects queued after syncFx is called on the element are
8726      * run concurrently.  This is the opposite of {@link #sequenceFx}.
8727      * @return {Ext.Element} The Element
8728      */
8729     syncFx : function(){
8730         var me = this;
8731         me.fxDefaults = Ext.apply(me.fxDefaults || {}, {
8732             block : FALSE,
8733             concurrent : TRUE,
8734             stopFx : FALSE
8735         });
8736         return me;
8737     },
8738
8739     /**
8740      * Ensures that all effects queued after sequenceFx is called on the element are
8741      * run in sequence.  This is the opposite of {@link #syncFx}.
8742      * @return {Ext.Element} The Element
8743      */
8744     sequenceFx : function(){
8745         var me = this;
8746         me.fxDefaults = Ext.apply(me.fxDefaults || {}, {
8747             block : FALSE,
8748             concurrent : FALSE,
8749             stopFx : FALSE
8750         });
8751         return me;
8752     },
8753
8754     /* @private */
8755     nextFx : function(){        
8756         var ef = getQueue(this.dom.id)[0];
8757         if(ef){
8758             ef.call(this);
8759         }
8760     },
8761
8762     /**
8763      * Returns true if the element has any effects actively running or queued, else returns false.
8764      * @return {Boolean} True if element has active effects, else false
8765      */
8766     hasActiveFx : function(){
8767         return getQueue(this.dom.id)[0];
8768     },
8769
8770     /**
8771      * Stops any running effects and clears the element's internal effects queue if it contains
8772      * any additional effects that haven't started yet.
8773      * @return {Ext.Element} The Element
8774      */
8775     stopFx : function(finish){
8776         var me = this,
8777             id = me.dom.id;
8778         if(me.hasActiveFx()){
8779             var cur = getQueue(id)[0];
8780             if(cur && cur.anim){
8781                 if(cur.anim.isAnimated){
8782                     setQueue(id, [cur]); //clear
8783                     cur.anim.stop(finish !== undefined ? finish : TRUE);
8784                 }else{
8785                     setQueue(id, []);
8786                 }
8787             }
8788         }
8789         return me;
8790     },
8791
8792     /* @private */
8793     beforeFx : function(o){
8794         if(this.hasActiveFx() && !o.concurrent){
8795            if(o.stopFx){
8796                this.stopFx();
8797                return TRUE;
8798            }
8799            return FALSE;
8800         }
8801         return TRUE;
8802     },
8803
8804     /**
8805      * Returns true if the element is currently blocking so that no other effect can be queued
8806      * until this effect is finished, else returns false if blocking is not set.  This is commonly
8807      * used to ensure that an effect initiated by a user action runs to completion prior to the
8808      * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
8809      * @return {Boolean} True if blocking, else false
8810      */
8811     hasFxBlock : function(){
8812         var q = getQueue(this.dom.id);
8813         return q && q[0] && q[0].block;
8814     },
8815
8816     /* @private */
8817     queueFx : function(o, fn){
8818         var me = fly(this.dom);
8819         if(!me.hasFxBlock()){
8820             Ext.applyIf(o, me.fxDefaults);
8821             if(!o.concurrent){
8822                 var run = me.beforeFx(o);
8823                 fn.block = o.block;
8824                 getQueue(me.dom.id).push(fn);
8825                 if(run){
8826                     me.nextFx();
8827                 }
8828             }else{
8829                 fn.call(me);
8830             }
8831         }
8832         return me;
8833     },
8834
8835     /* @private */
8836     fxWrap : function(pos, o, vis){ 
8837         var dom = this.dom,
8838             wrap,
8839             wrapXY;
8840         if(!o.wrap || !(wrap = Ext.getDom(o.wrap))){            
8841             if(o.fixPosition){
8842                 wrapXY = fly(dom).getXY();
8843             }
8844             var div = document.createElement("div");
8845             div.style.visibility = vis;
8846             wrap = dom.parentNode.insertBefore(div, dom);
8847             fly(wrap).setPositioning(pos);
8848             if(fly(wrap).isStyle(POSITION, "static")){
8849                 fly(wrap).position("relative");
8850             }
8851             fly(dom).clearPositioning('auto');
8852             fly(wrap).clip();
8853             wrap.appendChild(dom);
8854             if(wrapXY){
8855                 fly(wrap).setXY(wrapXY);
8856             }
8857         }
8858         return wrap;
8859     },
8860
8861     /* @private */
8862     fxUnwrap : function(wrap, pos, o){      
8863         var dom = this.dom;
8864         fly(dom).clearPositioning();
8865         fly(dom).setPositioning(pos);
8866         if(!o.wrap){
8867             var pn = fly(wrap).dom.parentNode;
8868             pn.insertBefore(dom, wrap); 
8869             fly(wrap).remove();
8870         }
8871     },
8872
8873     /* @private */
8874     getFxRestore : function(){
8875         var st = this.dom.style;
8876         return {pos: this.getPositioning(), width: st.width, height : st.height};
8877     },
8878
8879     /* @private */
8880     afterFx : function(o){
8881         var dom = this.dom,
8882             id = dom.id;
8883         if(o.afterStyle){
8884             fly(dom).setStyle(o.afterStyle);            
8885         }
8886         if(o.afterCls){
8887             fly(dom).addClass(o.afterCls);
8888         }
8889         if(o.remove == TRUE){
8890             fly(dom).remove();
8891         }
8892         if(o.callback){
8893             o.callback.call(o.scope, fly(dom));
8894         }
8895         if(!o.concurrent){
8896             getQueue(id).shift();
8897             fly(dom).nextFx();
8898         }
8899     },
8900
8901     /* @private */
8902     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
8903         animType = animType || 'run';
8904         opt = opt || {};
8905         var anim = Ext.lib.Anim[animType](
8906                 this.dom, 
8907                 args,
8908                 (opt.duration || defaultDur) || .35,
8909                 (opt.easing || defaultEase) || EASEOUT,
8910                 cb,            
8911                 this
8912             );
8913         opt.anim = anim;
8914         return anim;
8915     }
8916 };
8917
8918 // backwards compat
8919 Ext.Fx.resize = Ext.Fx.scale;
8920
8921 //When included, Ext.Fx is automatically applied to Element so that all basic
8922 //effects are available directly via the Element API
8923 Ext.Element.addMethods(Ext.Fx);
8924 })();
8925 /**
8926  * @class Ext.CompositeElementLite
8927  * <p>This class encapsulates a <i>collection</i> of DOM elements, providing methods to filter
8928  * members, or to perform collective actions upon the whole set.</p>
8929  * <p>Although they are not listed, this class supports all of the methods of {@link Ext.Element} and
8930  * {@link Ext.Fx}. The methods from these classes will be performed on all the elements in this collection.</p>
8931  * Example:<pre><code>
8932 var els = Ext.select("#some-el div.some-class");
8933 // or select directly from an existing element
8934 var el = Ext.get('some-el');
8935 el.select('div.some-class');
8936
8937 els.setWidth(100); // all elements become 100 width
8938 els.hide(true); // all elements fade out and hide
8939 // or
8940 els.setWidth(100).hide(true);
8941 </code>
8942  */
8943 Ext.CompositeElementLite = function(els, root){
8944     /**
8945      * <p>The Array of DOM elements which this CompositeElement encapsulates. Read-only.</p>
8946      * <p>This will not <i>usually</i> be accessed in developers' code, but developers wishing
8947      * to augment the capabilities of the CompositeElementLite class may use it when adding
8948      * methods to the class.</p>
8949      * <p>For example to add the <code>nextAll</code> method to the class to <b>add</b> all
8950      * following siblings of selected elements, the code would be</p><code><pre>
8951 Ext.override(Ext.CompositeElementLite, {
8952     nextAll: function() {
8953         var els = this.elements, i, l = els.length, n, r = [], ri = -1;
8954
8955 //      Loop through all elements in this Composite, accumulating
8956 //      an Array of all siblings.
8957         for (i = 0; i < l; i++) {
8958             for (n = els[i].nextSibling; n; n = n.nextSibling) {
8959                 r[++ri] = n;
8960             }
8961         }
8962
8963 //      Add all found siblings to this Composite
8964         return this.add(r);
8965     }
8966 });</pre></code>
8967      * @type Array
8968      * @property elements
8969      */
8970     this.elements = [];
8971     this.add(els, root);
8972     this.el = new Ext.Element.Flyweight();
8973 };
8974
8975 Ext.CompositeElementLite.prototype = {
8976     isComposite: true,
8977
8978     // private
8979     getElement : function(el){
8980         // Set the shared flyweight dom property to the current element
8981         var e = this.el;
8982         e.dom = el;
8983         e.id = el.id;
8984         return e;
8985     },
8986
8987     // private
8988     transformElement : function(el){
8989         return Ext.getDom(el);
8990     },
8991
8992     /**
8993      * Returns the number of elements in this Composite.
8994      * @return Number
8995      */
8996     getCount : function(){
8997         return this.elements.length;
8998     },
8999     /**
9000      * Adds elements to this Composite object.
9001      * @param {Mixed} els Either an Array of DOM elements to add, or another Composite object who's elements should be added.
9002      * @return {CompositeElement} This Composite object.
9003      */
9004     add : function(els, root){
9005         var me = this,
9006             elements = me.elements;
9007         if(!els){
9008             return this;
9009         }
9010         if(typeof els == "string"){
9011             els = Ext.Element.selectorFunction(els, root);
9012         }else if(els.isComposite){
9013             els = els.elements;
9014         }else if(!Ext.isIterable(els)){
9015             els = [els];
9016         }
9017
9018         for(var i = 0, len = els.length; i < len; ++i){
9019             elements.push(me.transformElement(els[i]));
9020         }
9021         return me;
9022     },
9023
9024     invoke : function(fn, args){
9025         var me = this,
9026             els = me.elements,
9027             len = els.length,
9028             e,
9029             i;
9030
9031         for(i = 0; i < len; i++) {
9032             e = els[i];
9033             if(e){
9034                 Ext.Element.prototype[fn].apply(me.getElement(e), args);
9035             }
9036         }
9037         return me;
9038     },
9039     /**
9040      * Returns a flyweight Element of the dom element object at the specified index
9041      * @param {Number} index
9042      * @return {Ext.Element}
9043      */
9044     item : function(index){
9045         var me = this,
9046             el = me.elements[index],
9047             out = null;
9048
9049         if(el){
9050             out = me.getElement(el);
9051         }
9052         return out;
9053     },
9054
9055     // fixes scope with flyweight
9056     addListener : function(eventName, handler, scope, opt){
9057         var els = this.elements,
9058             len = els.length,
9059             i, e;
9060
9061         for(i = 0; i<len; i++) {
9062             e = els[i];
9063             if(e) {
9064                 Ext.EventManager.on(e, eventName, handler, scope || e, opt);
9065             }
9066         }
9067         return this;
9068     },
9069     /**
9070      * <p>Calls the passed function for each element in this composite.</p>
9071      * @param {Function} fn The function to call. The function is passed the following parameters:<ul>
9072      * <li><b>el</b> : Element<div class="sub-desc">The current Element in the iteration.
9073      * <b>This is the flyweight (shared) Ext.Element instance, so if you require a
9074      * a reference to the dom node, use el.dom.</b></div></li>
9075      * <li><b>c</b> : Composite<div class="sub-desc">This Composite object.</div></li>
9076      * <li><b>idx</b> : Number<div class="sub-desc">The zero-based index in the iteration.</div></li>
9077      * </ul>
9078      * @param {Object} scope (optional) The scope (<i>this</i> reference) in which the function is executed. (defaults to the Element)
9079      * @return {CompositeElement} this
9080      */
9081     each : function(fn, scope){
9082         var me = this,
9083             els = me.elements,
9084             len = els.length,
9085             i, e;
9086
9087         for(i = 0; i<len; i++) {
9088             e = els[i];
9089             if(e){
9090                 e = this.getElement(e);
9091                 if(fn.call(scope || e, e, me, i) === false){
9092                     break;
9093                 }
9094             }
9095         }
9096         return me;
9097     },
9098
9099     /**
9100     * Clears this Composite and adds the elements passed.
9101     * @param {Mixed} els Either an array of DOM elements, or another Composite from which to fill this Composite.
9102     * @return {CompositeElement} this
9103     */
9104     fill : function(els){
9105         var me = this;
9106         me.elements = [];
9107         me.add(els);
9108         return me;
9109     },
9110
9111     /**
9112      * Filters this composite to only elements that match the passed selector.
9113      * @param {String/Function} selector A string CSS selector or a comparison function.
9114      * The comparison function will be called with the following arguments:<ul>
9115      * <li><code>el</code> : Ext.Element<div class="sub-desc">The current DOM element.</div></li>
9116      * <li><code>index</code> : Number<div class="sub-desc">The current index within the collection.</div></li>
9117      * </ul>
9118      * @return {CompositeElement} this
9119      */
9120     filter : function(selector){
9121         var els = [],
9122             me = this,
9123             fn = Ext.isFunction(selector) ? selector
9124                 : function(el){
9125                     return el.is(selector);
9126                 };
9127
9128         me.each(function(el, self, i) {
9129             if (fn(el, i) !== false) {
9130                 els[els.length] = me.transformElement(el);
9131             }
9132         });
9133         
9134         me.elements = els;
9135         return me;
9136     },
9137
9138     /**
9139      * Find the index of the passed element within the composite collection.
9140      * @param el {Mixed} The id of an element, or an Ext.Element, or an HtmlElement to find within the composite collection.
9141      * @return Number The index of the passed Ext.Element in the composite collection, or -1 if not found.
9142      */
9143     indexOf : function(el){
9144         return this.elements.indexOf(this.transformElement(el));
9145     },
9146
9147     /**
9148     * Replaces the specified element with the passed element.
9149     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
9150     * to replace.
9151     * @param {Mixed} replacement The id of an element or the Element itself.
9152     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
9153     * @return {CompositeElement} this
9154     */
9155     replaceElement : function(el, replacement, domReplace){
9156         var index = !isNaN(el) ? el : this.indexOf(el),
9157             d;
9158         if(index > -1){
9159             replacement = Ext.getDom(replacement);
9160             if(domReplace){
9161                 d = this.elements[index];
9162                 d.parentNode.insertBefore(replacement, d);
9163                 Ext.removeNode(d);
9164             }
9165             this.elements.splice(index, 1, replacement);
9166         }
9167         return this;
9168     },
9169
9170     /**
9171      * Removes all elements.
9172      */
9173     clear : function(){
9174         this.elements = [];
9175     }
9176 };
9177
9178 Ext.CompositeElementLite.prototype.on = Ext.CompositeElementLite.prototype.addListener;
9179
9180 (function(){
9181 var fnName,
9182     ElProto = Ext.Element.prototype,
9183     CelProto = Ext.CompositeElementLite.prototype;
9184
9185 for(fnName in ElProto){
9186     if(Ext.isFunction(ElProto[fnName])){
9187         (function(fnName){
9188             CelProto[fnName] = CelProto[fnName] || function(){
9189                 return this.invoke(fnName, arguments);
9190             };
9191         }).call(CelProto, fnName);
9192
9193     }
9194 }
9195 })();
9196
9197 if(Ext.DomQuery){
9198     Ext.Element.selectorFunction = Ext.DomQuery.select;
9199 }
9200
9201 /**
9202  * Selects elements based on the passed CSS selector to enable {@link Ext.Element Element} methods
9203  * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or
9204  * {@link Ext.CompositeElementLite CompositeElementLite} object.
9205  * @param {String/Array} selector The CSS selector or an array of elements
9206  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9207  * @return {CompositeElementLite/CompositeElement}
9208  * @member Ext.Element
9209  * @method select
9210  */
9211 Ext.Element.select = function(selector, root){
9212     var els;
9213     if(typeof selector == "string"){
9214         els = Ext.Element.selectorFunction(selector, root);
9215     }else if(selector.length !== undefined){
9216         els = selector;
9217     }else{
9218         throw "Invalid selector";
9219     }
9220     return new Ext.CompositeElementLite(els);
9221 };
9222 /**
9223  * Selects elements based on the passed CSS selector to enable {@link Ext.Element Element} methods
9224  * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or
9225  * {@link Ext.CompositeElementLite CompositeElementLite} object.
9226  * @param {String/Array} selector The CSS selector or an array of elements
9227  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9228  * @return {CompositeElementLite/CompositeElement}
9229  * @member Ext
9230  * @method select
9231  */
9232 Ext.select = Ext.Element.select;
9233 /**
9234  * @class Ext.CompositeElementLite
9235  */
9236 Ext.apply(Ext.CompositeElementLite.prototype, {
9237     addElements : function(els, root){
9238         if(!els){
9239             return this;
9240         }
9241         if(typeof els == "string"){
9242             els = Ext.Element.selectorFunction(els, root);
9243         }
9244         var yels = this.elements;
9245         Ext.each(els, function(e) {
9246             yels.push(Ext.get(e));
9247         });
9248         return this;
9249     },
9250
9251     /**
9252      * Returns the first Element
9253      * @return {Ext.Element}
9254      */
9255     first : function(){
9256         return this.item(0);
9257     },
9258
9259     /**
9260      * Returns the last Element
9261      * @return {Ext.Element}
9262      */
9263     last : function(){
9264         return this.item(this.getCount()-1);
9265     },
9266
9267     /**
9268      * Returns true if this composite contains the passed element
9269      * @param el {Mixed} The id of an element, or an Ext.Element, or an HtmlElement to find within the composite collection.
9270      * @return Boolean
9271      */
9272     contains : function(el){
9273         return this.indexOf(el) != -1;
9274     },
9275
9276     /**
9277     * Removes the specified element(s).
9278     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
9279     * or an array of any of those.
9280     * @param {Boolean} removeDom (optional) True to also remove the element from the document
9281     * @return {CompositeElement} this
9282     */
9283     removeElement : function(keys, removeDom){
9284         var me = this,
9285             els = this.elements,
9286             el;
9287         Ext.each(keys, function(val){
9288             if ((el = (els[val] || els[val = me.indexOf(val)]))) {
9289                 if(removeDom){
9290                     if(el.dom){
9291                         el.remove();
9292                     }else{
9293                         Ext.removeNode(el);
9294                     }
9295                 }
9296                 els.splice(val, 1);
9297             }
9298         });
9299         return this;
9300     }
9301 });
9302 /**
9303  * @class Ext.CompositeElement
9304  * @extends Ext.CompositeElementLite
9305  * <p>This class encapsulates a <i>collection</i> of DOM elements, providing methods to filter
9306  * members, or to perform collective actions upon the whole set.</p>
9307  * <p>Although they are not listed, this class supports all of the methods of {@link Ext.Element} and
9308  * {@link Ext.Fx}. The methods from these classes will be performed on all the elements in this collection.</p>
9309  * <p>All methods return <i>this</i> and can be chained.</p>
9310  * Usage:
9311 <pre><code>
9312 var els = Ext.select("#some-el div.some-class", true);
9313 // or select directly from an existing element
9314 var el = Ext.get('some-el');
9315 el.select('div.some-class', true);
9316
9317 els.setWidth(100); // all elements become 100 width
9318 els.hide(true); // all elements fade out and hide
9319 // or
9320 els.setWidth(100).hide(true);
9321 </code></pre>
9322  */
9323 Ext.CompositeElement = Ext.extend(Ext.CompositeElementLite, {
9324     
9325     constructor : function(els, root){
9326         this.elements = [];
9327         this.add(els, root);
9328     },
9329     
9330     // private
9331     getElement : function(el){
9332         // In this case just return it, since we already have a reference to it
9333         return el;
9334     },
9335     
9336     // private
9337     transformElement : function(el){
9338         return Ext.get(el);
9339     }
9340
9341     /**
9342     * Adds elements to this composite.
9343     * @param {String/Array} els A string CSS selector, an array of elements or an element
9344     * @return {CompositeElement} this
9345     */
9346
9347     /**
9348      * Returns the Element object at the specified index
9349      * @param {Number} index
9350      * @return {Ext.Element}
9351      */
9352
9353     /**
9354      * Iterates each <code>element</code> in this <code>composite</code>
9355      * calling the supplied function using {@link Ext#each}.
9356      * @param {Function} fn The function to be called with each
9357      * <code>element</code>. If the supplied function returns <tt>false</tt>,
9358      * iteration stops. This function is called with the following arguments:
9359      * <div class="mdetail-params"><ul>
9360      * <li><code>element</code> : <i>Ext.Element</i><div class="sub-desc">The element at the current <code>index</code>
9361      * in the <code>composite</code></div></li>
9362      * <li><code>composite</code> : <i>Object</i> <div class="sub-desc">This composite.</div></li>
9363      * <li><code>index</code> : <i>Number</i> <div class="sub-desc">The current index within the <code>composite</code> </div></li>
9364      * </ul></div>
9365      * @param {Object} scope (optional) The scope (<code><this</code> reference) in which the specified function is executed.
9366      * Defaults to the <code>element</code> at the current <code>index</code>
9367      * within the composite.
9368      * @return {CompositeElement} this
9369      */
9370 });
9371
9372 /**
9373  * Selects elements based on the passed CSS selector to enable {@link Ext.Element Element} methods
9374  * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or
9375  * {@link Ext.CompositeElementLite CompositeElementLite} object.
9376  * @param {String/Array} selector The CSS selector or an array of elements
9377  * @param {Boolean} unique (optional) true to create a unique Ext.Element for each element (defaults to a shared flyweight object)
9378  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9379  * @return {CompositeElementLite/CompositeElement}
9380  * @member Ext.Element
9381  * @method select
9382  */
9383 Ext.Element.select = function(selector, unique, root){
9384     var els;
9385     if(typeof selector == "string"){
9386         els = Ext.Element.selectorFunction(selector, root);
9387     }else if(selector.length !== undefined){
9388         els = selector;
9389     }else{
9390         throw "Invalid selector";
9391     }
9392
9393     return (unique === true) ? new Ext.CompositeElement(els) : new Ext.CompositeElementLite(els);
9394 };
9395
9396 /**
9397  * Selects elements based on the passed CSS selector to enable {@link Ext.Element Element} methods
9398  * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or
9399  * {@link Ext.CompositeElementLite CompositeElementLite} object.
9400  * @param {String/Array} selector The CSS selector or an array of elements
9401  * @param {Boolean} unique (optional) true to create a unique Ext.Element for each element (defaults to a shared flyweight object)
9402  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9403  * @return {CompositeElementLite/CompositeElement}
9404  * @member Ext
9405  * @method select
9406  */
9407 Ext.select = Ext.Element.select;(function(){
9408     var BEFOREREQUEST = "beforerequest",
9409         REQUESTCOMPLETE = "requestcomplete",
9410         REQUESTEXCEPTION = "requestexception",
9411         UNDEFINED = undefined,
9412         LOAD = 'load',
9413         POST = 'POST',
9414         GET = 'GET',
9415         WINDOW = window;
9416
9417     /**
9418      * @class Ext.data.Connection
9419      * @extends Ext.util.Observable
9420      * <p>The class encapsulates a connection to the page's originating domain, allowing requests to be made
9421      * either to a configured URL, or to a URL specified at request time.</p>
9422      * <p>Requests made by this class are asynchronous, and will return immediately. No data from
9423      * the server will be available to the statement immediately following the {@link #request} call.
9424      * To process returned data, use a
9425      * <a href="#request-option-success" ext:member="request-option-success" ext:cls="Ext.data.Connection">success callback</a>
9426      * in the request options object,
9427      * or an {@link #requestcomplete event listener}.</p>
9428      * <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
9429      * is they are <b>not</b> performed using XMLHttpRequests. Instead the form is submitted in the standard
9430      * manner with the DOM <tt>&lt;form></tt> element temporarily modified to have its
9431      * <a href="http://www.w3.org/TR/REC-html40/present/frames.html#adef-target">target</a> set to refer
9432      * to a dynamically generated, hidden <tt>&lt;iframe></tt> which is inserted into the document
9433      * but removed after the return data has been gathered.</p>
9434      * <p>The server response is parsed by the browser to create the document for the IFRAME. If the
9435      * server is using JSON to send the return object, then the
9436      * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.17">Content-Type</a> header
9437      * must be set to "text/html" in order to tell the browser to insert the text unchanged into the document body.</p>
9438      * <p>Characters which are significant to an HTML parser must be sent as HTML entities, so encode
9439      * "&lt;" as "&amp;lt;", "&amp;" as "&amp;amp;" etc.</p>
9440      * <p>The response text is retrieved from the document, and a fake XMLHttpRequest object
9441      * is created containing a <tt>responseText</tt> property in order to conform to the
9442      * requirements of event handlers and callbacks.</p>
9443      * <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>
9444      * and some server technologies (notably JEE) may require some custom processing in order to
9445      * retrieve parameter names and parameter values from the packet content.</p>
9446      * <p>Also note that it's not possible to check the response code of the hidden iframe, so the success handler will ALWAYS fire.</p>
9447      * @constructor
9448      * @param {Object} config a configuration object.
9449      */
9450     Ext.data.Connection = function(config){
9451         Ext.apply(this, config);
9452         this.addEvents(
9453             /**
9454              * @event beforerequest
9455              * Fires before a network request is made to retrieve a data object.
9456              * @param {Connection} conn This Connection object.
9457              * @param {Object} options The options config object passed to the {@link #request} method.
9458              */
9459             BEFOREREQUEST,
9460             /**
9461              * @event requestcomplete
9462              * Fires if the request was successfully completed.
9463              * @param {Connection} conn This Connection object.
9464              * @param {Object} response The XHR object containing the response data.
9465              * See <a href="http://www.w3.org/TR/XMLHttpRequest/">The XMLHttpRequest Object</a>
9466              * for details.
9467              * @param {Object} options The options config object passed to the {@link #request} method.
9468              */
9469             REQUESTCOMPLETE,
9470             /**
9471              * @event requestexception
9472              * Fires if an error HTTP status was returned from the server.
9473              * See <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html">HTTP Status Code Definitions</a>
9474              * for details of HTTP status codes.
9475              * @param {Connection} conn This Connection object.
9476              * @param {Object} response The XHR object containing the response data.
9477              * See <a href="http://www.w3.org/TR/XMLHttpRequest/">The XMLHttpRequest Object</a>
9478              * for details.
9479              * @param {Object} options The options config object passed to the {@link #request} method.
9480              */
9481             REQUESTEXCEPTION
9482         );
9483         Ext.data.Connection.superclass.constructor.call(this);
9484     };
9485
9486     Ext.extend(Ext.data.Connection, Ext.util.Observable, {
9487         /**
9488          * @cfg {String} url (Optional) <p>The default URL to be used for requests to the server. Defaults to undefined.</p>
9489          * <p>The <code>url</code> config may be a function which <i>returns</i> the URL to use for the Ajax request. The scope
9490          * (<code><b>this</b></code> reference) of the function is the <code>scope</code> option passed to the {@link #request} method.</p>
9491          */
9492         /**
9493          * @cfg {Object} extraParams (Optional) An object containing properties which are used as
9494          * extra parameters to each request made by this object. (defaults to undefined)
9495          */
9496         /**
9497          * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
9498          *  to each request made by this object. (defaults to undefined)
9499          */
9500         /**
9501          * @cfg {String} method (Optional) The default HTTP method to be used for requests.
9502          * (defaults to undefined; if not set, but {@link #request} params are present, POST will be used;
9503          * otherwise, GET will be used.)
9504          */
9505         /**
9506          * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
9507          */
9508         timeout : 30000,
9509         /**
9510          * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
9511          * @type Boolean
9512          */
9513         autoAbort:false,
9514
9515         /**
9516          * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
9517          * @type Boolean
9518          */
9519         disableCaching: true,
9520
9521         /**
9522          * @cfg {String} disableCachingParam (Optional) Change the parameter which is sent went disabling caching
9523          * through a cache buster. Defaults to '_dc'
9524          * @type String
9525          */
9526         disableCachingParam: '_dc',
9527
9528         /**
9529          * <p>Sends an HTTP request to a remote server.</p>
9530          * <p><b>Important:</b> Ajax server requests are asynchronous, and this call will
9531          * return before the response has been received. Process any returned data
9532          * in a callback function.</p>
9533          * <pre><code>
9534 Ext.Ajax.request({
9535    url: 'ajax_demo/sample.json',
9536    success: function(response, opts) {
9537       var obj = Ext.decode(response.responseText);
9538       console.dir(obj);
9539    },
9540    failure: function(response, opts) {
9541       console.log('server-side failure with status code ' + response.status);
9542    }
9543 });
9544          * </code></pre>
9545          * <p>To execute a callback function in the correct scope, use the <tt>scope</tt> option.</p>
9546          * @param {Object} options An object which may contain the following properties:<ul>
9547          * <li><b>url</b> : String/Function (Optional)<div class="sub-desc">The URL to
9548          * which to send the request, or a function to call which returns a URL string. The scope of the
9549          * function is specified by the <tt>scope</tt> option. Defaults to the configured
9550          * <tt>{@link #url}</tt>.</div></li>
9551          * <li><b>params</b> : Object/String/Function (Optional)<div class="sub-desc">
9552          * An object containing properties which are used as parameters to the
9553          * request, a url encoded string or a function to call to get either. The scope of the function
9554          * is specified by the <tt>scope</tt> option.</div></li>
9555          * <li><b>method</b> : String (Optional)<div class="sub-desc">The HTTP method to use
9556          * for the request. Defaults to the configured method, or if no method was configured,
9557          * "GET" if no parameters are being sent, and "POST" if parameters are being sent.  Note that
9558          * the method name is case-sensitive and should be all caps.</div></li>
9559          * <li><b>callback</b> : Function (Optional)<div class="sub-desc">The
9560          * function to be called upon receipt of the HTTP response. The callback is
9561          * called regardless of success or failure and is passed the following
9562          * parameters:<ul>
9563          * <li><b>options</b> : Object<div class="sub-desc">The parameter to the request call.</div></li>
9564          * <li><b>success</b> : Boolean<div class="sub-desc">True if the request succeeded.</div></li>
9565          * <li><b>response</b> : Object<div class="sub-desc">The XMLHttpRequest object containing the response data.
9566          * See <a href="http://www.w3.org/TR/XMLHttpRequest/">http://www.w3.org/TR/XMLHttpRequest/</a> for details about
9567          * accessing elements of the response.</div></li>
9568          * </ul></div></li>
9569          * <li><a id="request-option-success"></a><b>success</b> : Function (Optional)<div class="sub-desc">The function
9570          * to be called upon success of the request. The callback is passed the following
9571          * parameters:<ul>
9572          * <li><b>response</b> : Object<div class="sub-desc">The XMLHttpRequest object containing the response data.</div></li>
9573          * <li><b>options</b> : Object<div class="sub-desc">The parameter to the request call.</div></li>
9574          * </ul></div></li>
9575          * <li><b>failure</b> : Function (Optional)<div class="sub-desc">The function
9576          * to be called upon failure of the request. The callback is passed the
9577          * following parameters:<ul>
9578          * <li><b>response</b> : Object<div class="sub-desc">The XMLHttpRequest object containing the response data.</div></li>
9579          * <li><b>options</b> : Object<div class="sub-desc">The parameter to the request call.</div></li>
9580          * </ul></div></li>
9581          * <li><b>scope</b> : Object (Optional)<div class="sub-desc">The scope in
9582          * which to execute the callbacks: The "this" object for the callback function. If the <tt>url</tt>, or <tt>params</tt> options were
9583          * specified as functions from which to draw values, then this also serves as the scope for those function calls.
9584          * Defaults to the browser window.</div></li>
9585          * <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>
9586          * <li><b>form</b> : Element/HTMLElement/String (Optional)<div class="sub-desc">The <tt>&lt;form&gt;</tt>
9587          * Element or the id of the <tt>&lt;form&gt;</tt> to pull parameters from.</div></li>
9588          * <li><a id="request-option-isUpload"></a><b>isUpload</b> : Boolean (Optional)<div class="sub-desc"><b>Only meaningful when used
9589          * with the <tt>form</tt> option</b>.
9590          * <p>True if the form object is a file upload (will be set automatically if the form was
9591          * configured with <b><tt>enctype</tt></b> "multipart/form-data").</p>
9592          * <p>File uploads are not performed using normal "Ajax" techniques, that is they are <b>not</b>
9593          * performed using XMLHttpRequests. Instead the form is submitted in the standard manner with the
9594          * DOM <tt>&lt;form></tt> element temporarily modified to have its
9595          * <a href="http://www.w3.org/TR/REC-html40/present/frames.html#adef-target">target</a> set to refer
9596          * to a dynamically generated, hidden <tt>&lt;iframe></tt> which is inserted into the document
9597          * but removed after the return data has been gathered.</p>
9598          * <p>The server response is parsed by the browser to create the document for the IFRAME. If the
9599          * server is using JSON to send the return object, then the
9600          * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.17">Content-Type</a> header
9601          * must be set to "text/html" in order to tell the browser to insert the text unchanged into the document body.</p>
9602          * <p>The response text is retrieved from the document, and a fake XMLHttpRequest object
9603          * is created containing a <tt>responseText</tt> property in order to conform to the
9604          * requirements of event handlers and callbacks.</p>
9605          * <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>
9606          * and some server technologies (notably JEE) may require some custom processing in order to
9607          * retrieve parameter names and parameter values from the packet content.</p>
9608          * </div></li>
9609          * <li><b>headers</b> : Object (Optional)<div class="sub-desc">Request
9610          * headers to set for the request.</div></li>
9611          * <li><b>xmlData</b> : Object (Optional)<div class="sub-desc">XML document
9612          * to use for the post. Note: This will be used instead of params for the post
9613          * data. Any params will be appended to the URL.</div></li>
9614          * <li><b>jsonData</b> : Object/String (Optional)<div class="sub-desc">JSON
9615          * data to use as the post. Note: This will be used instead of params for the post
9616          * data. Any params will be appended to the URL.</div></li>
9617          * <li><b>disableCaching</b> : Boolean (Optional)<div class="sub-desc">True
9618          * to add a unique cache-buster param to GET requests.</div></li>
9619          * </ul></p>
9620          * <p>The options object may also contain any other property which might be needed to perform
9621          * postprocessing in a callback because it is passed to callback functions.</p>
9622          * @return {Number} transactionId The id of the server transaction. This may be used
9623          * to cancel the request.
9624          */
9625         request : function(o){
9626             var me = this;
9627             if(me.fireEvent(BEFOREREQUEST, me, o)){
9628                 if (o.el) {
9629                     if(!Ext.isEmpty(o.indicatorText)){
9630                         me.indicatorText = '<div class="loading-indicator">'+o.indicatorText+"</div>";
9631                     }
9632                     if(me.indicatorText) {
9633                         Ext.getDom(o.el).innerHTML = me.indicatorText;
9634                     }
9635                     o.success = (Ext.isFunction(o.success) ? o.success : function(){}).createInterceptor(function(response) {
9636                         Ext.getDom(o.el).innerHTML = response.responseText;
9637                     });
9638                 }
9639
9640                 var p = o.params,
9641                     url = o.url || me.url,
9642                     method,
9643                     cb = {success: me.handleResponse,
9644                           failure: me.handleFailure,
9645                           scope: me,
9646                           argument: {options: o},
9647                           timeout : o.timeout || me.timeout
9648                     },
9649                     form,
9650                     serForm;
9651
9652
9653                 if (Ext.isFunction(p)) {
9654                     p = p.call(o.scope||WINDOW, o);
9655                 }
9656
9657                 p = Ext.urlEncode(me.extraParams, Ext.isObject(p) ? Ext.urlEncode(p) : p);
9658
9659                 if (Ext.isFunction(url)) {
9660                     url = url.call(o.scope || WINDOW, o);
9661                 }
9662
9663                 if((form = Ext.getDom(o.form))){
9664                     url = url || form.action;
9665                      if(o.isUpload || /multipart\/form-data/i.test(form.getAttribute("enctype"))) {
9666                          return me.doFormUpload.call(me, o, p, url);
9667                      }
9668                     serForm = Ext.lib.Ajax.serializeForm(form);
9669                     p = p ? (p + '&' + serForm) : serForm;
9670                 }
9671
9672                 method = o.method || me.method || ((p || o.xmlData || o.jsonData) ? POST : GET);
9673
9674                 if(method === GET && (me.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
9675                     var dcp = o.disableCachingParam || me.disableCachingParam;
9676                     url = Ext.urlAppend(url, dcp + '=' + (new Date().getTime()));
9677                 }
9678
9679                 o.headers = Ext.apply(o.headers || {}, me.defaultHeaders || {});
9680
9681                 if(o.autoAbort === true || me.autoAbort) {
9682                     me.abort();
9683                 }
9684
9685                 if((method == GET || o.xmlData || o.jsonData) && p){
9686                     url = Ext.urlAppend(url, p);
9687                     p = '';
9688                 }
9689                 return (me.transId = Ext.lib.Ajax.request(method, url, cb, p, o));
9690             }else{
9691                 return o.callback ? o.callback.apply(o.scope, [o,UNDEFINED,UNDEFINED]) : null;
9692             }
9693         },
9694
9695         /**
9696          * Determine whether this object has a request outstanding.
9697          * @param {Number} transactionId (Optional) defaults to the last transaction
9698          * @return {Boolean} True if there is an outstanding request.
9699          */
9700         isLoading : function(transId){
9701             return transId ? Ext.lib.Ajax.isCallInProgress(transId) : !! this.transId;
9702         },
9703
9704         /**
9705          * Aborts any outstanding request.
9706          * @param {Number} transactionId (Optional) defaults to the last transaction
9707          */
9708         abort : function(transId){
9709             if(transId || this.isLoading()){
9710                 Ext.lib.Ajax.abort(transId || this.transId);
9711             }
9712         },
9713
9714         // private
9715         handleResponse : function(response){
9716             this.transId = false;
9717             var options = response.argument.options;
9718             response.argument = options ? options.argument : null;
9719             this.fireEvent(REQUESTCOMPLETE, this, response, options);
9720             if(options.success){
9721                 options.success.call(options.scope, response, options);
9722             }
9723             if(options.callback){
9724                 options.callback.call(options.scope, options, true, response);
9725             }
9726         },
9727
9728         // private
9729         handleFailure : function(response, e){
9730             this.transId = false;
9731             var options = response.argument.options;
9732             response.argument = options ? options.argument : null;
9733             this.fireEvent(REQUESTEXCEPTION, this, response, options, e);
9734             if(options.failure){
9735                 options.failure.call(options.scope, response, options);
9736             }
9737             if(options.callback){
9738                 options.callback.call(options.scope, options, false, response);
9739             }
9740         },
9741
9742         // private
9743         doFormUpload : function(o, ps, url){
9744             var id = Ext.id(),
9745                 doc = document,
9746                 frame = doc.createElement('iframe'),
9747                 form = Ext.getDom(o.form),
9748                 hiddens = [],
9749                 hd,
9750                 encoding = 'multipart/form-data',
9751                 buf = {
9752                     target: form.target,
9753                     method: form.method,
9754                     encoding: form.encoding,
9755                     enctype: form.enctype,
9756                     action: form.action
9757                 };
9758
9759             /*
9760              * Originally this behaviour was modified for Opera 10 to apply the secure URL after
9761              * the frame had been added to the document. It seems this has since been corrected in
9762              * Opera so the behaviour has been reverted, the URL will be set before being added.
9763              */
9764             Ext.fly(frame).set({
9765                 id: id,
9766                 name: id,
9767                 cls: 'x-hidden',
9768                 src: Ext.SSL_SECURE_URL
9769             }); 
9770
9771             doc.body.appendChild(frame);
9772
9773             // This is required so that IE doesn't pop the response up in a new window.
9774             if(Ext.isIE){
9775                document.frames[id].name = id;
9776             }
9777
9778
9779             Ext.fly(form).set({
9780                 target: id,
9781                 method: POST,
9782                 enctype: encoding,
9783                 encoding: encoding,
9784                 action: url || buf.action
9785             });
9786
9787             // add dynamic params
9788             Ext.iterate(Ext.urlDecode(ps, false), function(k, v){
9789                 hd = doc.createElement('input');
9790                 Ext.fly(hd).set({
9791                     type: 'hidden',
9792                     value: v,
9793                     name: k
9794                 });
9795                 form.appendChild(hd);
9796                 hiddens.push(hd);
9797             });
9798
9799             function cb(){
9800                 var me = this,
9801                     // bogus response object
9802                     r = {responseText : '',
9803                          responseXML : null,
9804                          argument : o.argument},
9805                     doc,
9806                     firstChild;
9807
9808                 try{
9809                     doc = frame.contentWindow.document || frame.contentDocument || WINDOW.frames[id].document;
9810                     if(doc){
9811                         if(doc.body){
9812                             if(/textarea/i.test((firstChild = doc.body.firstChild || {}).tagName)){ // json response wrapped in textarea
9813                                 r.responseText = firstChild.value;
9814                             }else{
9815                                 r.responseText = doc.body.innerHTML;
9816                             }
9817                         }
9818                         //in IE the document may still have a body even if returns XML.
9819                         r.responseXML = doc.XMLDocument || doc;
9820                     }
9821                 }
9822                 catch(e) {}
9823
9824                 Ext.EventManager.removeListener(frame, LOAD, cb, me);
9825
9826                 me.fireEvent(REQUESTCOMPLETE, me, r, o);
9827
9828                 function runCallback(fn, scope, args){
9829                     if(Ext.isFunction(fn)){
9830                         fn.apply(scope, args);
9831                     }
9832                 }
9833
9834                 runCallback(o.success, o.scope, [r, o]);
9835                 runCallback(o.callback, o.scope, [o, true, r]);
9836
9837                 if(!me.debugUploads){
9838                     setTimeout(function(){Ext.removeNode(frame);}, 100);
9839                 }
9840             }
9841
9842             Ext.EventManager.on(frame, LOAD, cb, this);
9843             form.submit();
9844
9845             Ext.fly(form).set(buf);
9846             Ext.each(hiddens, function(h) {
9847                 Ext.removeNode(h);
9848             });
9849         }
9850     });
9851 })();
9852
9853 /**
9854  * @class Ext.Ajax
9855  * @extends Ext.data.Connection
9856  * <p>The global Ajax request class that provides a simple way to make Ajax requests
9857  * with maximum flexibility.</p>
9858  * <p>Since Ext.Ajax is a singleton, you can set common properties/events for it once
9859  * and override them at the request function level only if necessary.</p>
9860  * <p>Common <b>Properties</b> you may want to set are:<div class="mdetail-params"><ul>
9861  * <li><b><tt>{@link #method}</tt></b><p class="sub-desc"></p></li>
9862  * <li><b><tt>{@link #extraParams}</tt></b><p class="sub-desc"></p></li>
9863  * <li><b><tt>{@link #url}</tt></b><p class="sub-desc"></p></li>
9864  * </ul></div>
9865  * <pre><code>
9866 // Default headers to pass in every request
9867 Ext.Ajax.defaultHeaders = {
9868     'Powered-By': 'Ext'
9869 };
9870  * </code></pre>
9871  * </p>
9872  * <p>Common <b>Events</b> you may want to set are:<div class="mdetail-params"><ul>
9873  * <li><b><tt>{@link Ext.data.Connection#beforerequest beforerequest}</tt></b><p class="sub-desc"></p></li>
9874  * <li><b><tt>{@link Ext.data.Connection#requestcomplete requestcomplete}</tt></b><p class="sub-desc"></p></li>
9875  * <li><b><tt>{@link Ext.data.Connection#requestexception requestexception}</tt></b><p class="sub-desc"></p></li>
9876  * </ul></div>
9877  * <pre><code>
9878 // Example: show a spinner during all Ajax requests
9879 Ext.Ajax.on('beforerequest', this.showSpinner, this);
9880 Ext.Ajax.on('requestcomplete', this.hideSpinner, this);
9881 Ext.Ajax.on('requestexception', this.hideSpinner, this);
9882  * </code></pre>
9883  * </p>
9884  * <p>An example request:</p>
9885  * <pre><code>
9886 // Basic request
9887 Ext.Ajax.{@link Ext.data.Connection#request request}({
9888    url: 'foo.php',
9889    success: someFn,
9890    failure: otherFn,
9891    headers: {
9892        'my-header': 'foo'
9893    },
9894    params: { foo: 'bar' }
9895 });
9896
9897 // Simple ajax form submission
9898 Ext.Ajax.{@link Ext.data.Connection#request request}({
9899     form: 'some-form',
9900     params: 'foo=bar'
9901 });
9902  * </code></pre>
9903  * </p>
9904  * @singleton
9905  */
9906 Ext.Ajax = new Ext.data.Connection({
9907     /**
9908      * @cfg {String} url @hide
9909      */
9910     /**
9911      * @cfg {Object} extraParams @hide
9912      */
9913     /**
9914      * @cfg {Object} defaultHeaders @hide
9915      */
9916     /**
9917      * @cfg {String} method (Optional) @hide
9918      */
9919     /**
9920      * @cfg {Number} timeout (Optional) @hide
9921      */
9922     /**
9923      * @cfg {Boolean} autoAbort (Optional) @hide
9924      */
9925
9926     /**
9927      * @cfg {Boolean} disableCaching (Optional) @hide
9928      */
9929
9930     /**
9931      * @property  disableCaching
9932      * True to add a unique cache-buster param to GET requests. (defaults to true)
9933      * @type Boolean
9934      */
9935     /**
9936      * @property  url
9937      * The default URL to be used for requests to the server. (defaults to undefined)
9938      * If the server receives all requests through one URL, setting this once is easier than
9939      * entering it on every request.
9940      * @type String
9941      */
9942     /**
9943      * @property  extraParams
9944      * An object containing properties which are used as extra parameters to each request made
9945      * by this object (defaults to undefined). Session information and other data that you need
9946      * to pass with each request are commonly put here.
9947      * @type Object
9948      */
9949     /**
9950      * @property  defaultHeaders
9951      * An object containing request headers which are added to each request made by this object
9952      * (defaults to undefined).
9953      * @type Object
9954      */
9955     /**
9956      * @property  method
9957      * The default HTTP method to be used for requests. Note that this is case-sensitive and
9958      * should be all caps (defaults to undefined; if not set but params are present will use
9959      * <tt>"POST"</tt>, otherwise will use <tt>"GET"</tt>.)
9960      * @type String
9961      */
9962     /**
9963      * @property  timeout
9964      * The timeout in milliseconds to be used for requests. (defaults to 30000)
9965      * @type Number
9966      */
9967
9968     /**
9969      * @property  autoAbort
9970      * Whether a new request should abort any pending requests. (defaults to false)
9971      * @type Boolean
9972      */
9973     autoAbort : false,
9974
9975     /**
9976      * Serialize the passed form into a url encoded string
9977      * @param {String/HTMLElement} form
9978      * @return {String}
9979      */
9980     serializeForm : function(form){
9981         return Ext.lib.Ajax.serializeForm(form);
9982     }
9983 });
9984 /**
9985  * @class Ext.Updater
9986  * @extends Ext.util.Observable
9987  * Provides AJAX-style update capabilities for Element objects.  Updater can be used to {@link #update}
9988  * an {@link Ext.Element} once, or you can use {@link #startAutoRefresh} to set up an auto-updating
9989  * {@link Ext.Element Element} on a specific interval.<br><br>
9990  * Usage:<br>
9991  * <pre><code>
9992  * var el = Ext.get("foo"); // Get Ext.Element object
9993  * var mgr = el.getUpdater();
9994  * mgr.update({
9995         url: "http://myserver.com/index.php",
9996         params: {
9997             param1: "foo",
9998             param2: "bar"
9999         }
10000  * });
10001  * ...
10002  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
10003  * <br>
10004  * // or directly (returns the same Updater instance)
10005  * var mgr = new Ext.Updater("myElementId");
10006  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
10007  * mgr.on("update", myFcnNeedsToKnow);
10008  * <br>
10009  * // short handed call directly from the element object
10010  * Ext.get("foo").load({
10011         url: "bar.php",
10012         scripts: true,
10013         params: "param1=foo&amp;param2=bar",
10014         text: "Loading Foo..."
10015  * });
10016  * </code></pre>
10017  * @constructor
10018  * Create new Updater directly.
10019  * @param {Mixed} el The element to update
10020  * @param {Boolean} forceNew (optional) By default the constructor checks to see if the passed element already
10021  * has an Updater and if it does it returns the same instance. This will skip that check (useful for extending this class).
10022  */
10023 Ext.UpdateManager = Ext.Updater = Ext.extend(Ext.util.Observable,
10024 function() {
10025     var BEFOREUPDATE = "beforeupdate",
10026         UPDATE = "update",
10027         FAILURE = "failure";
10028
10029     // private
10030     function processSuccess(response){
10031         var me = this;
10032         me.transaction = null;
10033         if (response.argument.form && response.argument.reset) {
10034             try { // put in try/catch since some older FF releases had problems with this
10035                 response.argument.form.reset();
10036             } catch(e){}
10037         }
10038         if (me.loadScripts) {
10039             me.renderer.render(me.el, response, me,
10040                updateComplete.createDelegate(me, [response]));
10041         } else {
10042             me.renderer.render(me.el, response, me);
10043             updateComplete.call(me, response);
10044         }
10045     }
10046
10047     // private
10048     function updateComplete(response, type, success){
10049         this.fireEvent(type || UPDATE, this.el, response);
10050         if(Ext.isFunction(response.argument.callback)){
10051             response.argument.callback.call(response.argument.scope, this.el, Ext.isEmpty(success) ? true : false, response, response.argument.options);
10052         }
10053     }
10054
10055     // private
10056     function processFailure(response){
10057         updateComplete.call(this, response, FAILURE, !!(this.transaction = null));
10058     }
10059
10060     return {
10061         constructor: function(el, forceNew){
10062             var me = this;
10063             el = Ext.get(el);
10064             if(!forceNew && el.updateManager){
10065                 return el.updateManager;
10066             }
10067             /**
10068              * The Element object
10069              * @type Ext.Element
10070              */
10071             me.el = el;
10072             /**
10073              * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
10074              * @type String
10075              */
10076             me.defaultUrl = null;
10077
10078             me.addEvents(
10079                 /**
10080                  * @event beforeupdate
10081                  * Fired before an update is made, return false from your handler and the update is cancelled.
10082                  * @param {Ext.Element} el
10083                  * @param {String/Object/Function} url
10084                  * @param {String/Object} params
10085                  */
10086                 BEFOREUPDATE,
10087                 /**
10088                  * @event update
10089                  * Fired after successful update is made.
10090                  * @param {Ext.Element} el
10091                  * @param {Object} oResponseObject The response Object
10092                  */
10093                 UPDATE,
10094                 /**
10095                  * @event failure
10096                  * Fired on update failure.
10097                  * @param {Ext.Element} el
10098                  * @param {Object} oResponseObject The response Object
10099                  */
10100                 FAILURE
10101             );
10102
10103             Ext.apply(me, Ext.Updater.defaults);
10104             /**
10105              * Blank page URL to use with SSL file uploads (defaults to {@link Ext.Updater.defaults#sslBlankUrl}).
10106              * @property sslBlankUrl
10107              * @type String
10108              */
10109             /**
10110              * Whether to append unique parameter on get request to disable caching (defaults to {@link Ext.Updater.defaults#disableCaching}).
10111              * @property disableCaching
10112              * @type Boolean
10113              */
10114             /**
10115              * Text for loading indicator (defaults to {@link Ext.Updater.defaults#indicatorText}).
10116              * @property indicatorText
10117              * @type String
10118              */
10119             /**
10120              * Whether to show indicatorText when loading (defaults to {@link Ext.Updater.defaults#showLoadIndicator}).
10121              * @property showLoadIndicator
10122              * @type String
10123              */
10124             /**
10125              * Timeout for requests or form posts in seconds (defaults to {@link Ext.Updater.defaults#timeout}).
10126              * @property timeout
10127              * @type Number
10128              */
10129             /**
10130              * True to process scripts in the output (defaults to {@link Ext.Updater.defaults#loadScripts}).
10131              * @property loadScripts
10132              * @type Boolean
10133              */
10134
10135             /**
10136              * Transaction object of the current executing transaction, or null if there is no active transaction.
10137              */
10138             me.transaction = null;
10139             /**
10140              * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
10141              * @type Function
10142              */
10143             me.refreshDelegate = me.refresh.createDelegate(me);
10144             /**
10145              * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
10146              * @type Function
10147              */
10148             me.updateDelegate = me.update.createDelegate(me);
10149             /**
10150              * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
10151              * @type Function
10152              */
10153             me.formUpdateDelegate = (me.formUpdate || function(){}).createDelegate(me);
10154
10155             /**
10156              * The renderer for this Updater (defaults to {@link Ext.Updater.BasicRenderer}).
10157              */
10158             me.renderer = me.renderer || me.getDefaultRenderer();
10159
10160             Ext.Updater.superclass.constructor.call(me);
10161         },
10162
10163         /**
10164          * Sets the content renderer for this Updater. See {@link Ext.Updater.BasicRenderer#render} for more details.
10165          * @param {Object} renderer The object implementing the render() method
10166          */
10167         setRenderer : function(renderer){
10168             this.renderer = renderer;
10169         },
10170
10171         /**
10172          * Returns the current content renderer for this Updater. See {@link Ext.Updater.BasicRenderer#render} for more details.
10173          * @return {Object}
10174          */
10175         getRenderer : function(){
10176            return this.renderer;
10177         },
10178
10179         /**
10180          * This is an overrideable method which returns a reference to a default
10181          * renderer class if none is specified when creating the Ext.Updater.
10182          * Defaults to {@link Ext.Updater.BasicRenderer}
10183          */
10184         getDefaultRenderer: function() {
10185             return new Ext.Updater.BasicRenderer();
10186         },
10187
10188         /**
10189          * Sets the default URL used for updates.
10190          * @param {String/Function} defaultUrl The url or a function to call to get the url
10191          */
10192         setDefaultUrl : function(defaultUrl){
10193             this.defaultUrl = defaultUrl;
10194         },
10195
10196         /**
10197          * Get the Element this Updater is bound to
10198          * @return {Ext.Element} The element
10199          */
10200         getEl : function(){
10201             return this.el;
10202         },
10203
10204         /**
10205          * Performs an <b>asynchronous</b> request, updating this element with the response.
10206          * If params are specified it uses POST, otherwise it uses GET.<br><br>
10207          * <b>Note:</b> Due to the asynchronous nature of remote server requests, the Element
10208          * will not have been fully updated when the function returns. To post-process the returned
10209          * data, use the callback option, or an <b><code>update</code></b> event handler.
10210          * @param {Object} options A config object containing any of the following options:<ul>
10211          * <li>url : <b>String/Function</b><p class="sub-desc">The URL to request or a function which
10212          * <i>returns</i> the URL (defaults to the value of {@link Ext.Ajax#url} if not specified).</p></li>
10213          * <li>method : <b>String</b><p class="sub-desc">The HTTP method to
10214          * use. Defaults to POST if the <code>params</code> argument is present, otherwise GET.</p></li>
10215          * <li>params : <b>String/Object/Function</b><p class="sub-desc">The
10216          * parameters to pass to the server (defaults to none). These may be specified as a url-encoded
10217          * string, or as an object containing properties which represent parameters,
10218          * or as a function, which returns such an object.</p></li>
10219          * <li>scripts : <b>Boolean</b><p class="sub-desc">If <code>true</code>
10220          * any &lt;script&gt; tags embedded in the response text will be extracted
10221          * and executed (defaults to {@link Ext.Updater.defaults#loadScripts}). If this option is specified,
10222          * the callback will be called <i>after</i> the execution of the scripts.</p></li>
10223          * <li>callback : <b>Function</b><p class="sub-desc">A function to
10224          * be called when the response from the server arrives. The following
10225          * parameters are passed:<ul>
10226          * <li><b>el</b> : Ext.Element<p class="sub-desc">The Element being updated.</p></li>
10227          * <li><b>success</b> : Boolean<p class="sub-desc">True for success, false for failure.</p></li>
10228          * <li><b>response</b> : XMLHttpRequest<p class="sub-desc">The XMLHttpRequest which processed the update.</p></li>
10229          * <li><b>options</b> : Object<p class="sub-desc">The config object passed to the update call.</p></li></ul>
10230          * </p></li>
10231          * <li>scope : <b>Object</b><p class="sub-desc">The scope in which
10232          * to execute the callback (The callback's <code>this</code> reference.) If the
10233          * <code>params</code> argument is a function, this scope is used for that function also.</p></li>
10234          * <li>discardUrl : <b>Boolean</b><p class="sub-desc">By default, the URL of this request becomes
10235          * the default URL for this Updater object, and will be subsequently used in {@link #refresh}
10236          * calls.  To bypass this behavior, pass <code>discardUrl:true</code> (defaults to false).</p></li>
10237          * <li>timeout : <b>Number</b><p class="sub-desc">The number of seconds to wait for a response before
10238          * timing out (defaults to {@link Ext.Updater.defaults#timeout}).</p></li>
10239          * <li>text : <b>String</b><p class="sub-desc">The text to use as the innerHTML of the
10240          * {@link Ext.Updater.defaults#indicatorText} div (defaults to 'Loading...').  To replace the entire div, not
10241          * just the text, override {@link Ext.Updater.defaults#indicatorText} directly.</p></li>
10242          * <li>nocache : <b>Boolean</b><p class="sub-desc">Only needed for GET
10243          * requests, this option causes an extra, auto-generated parameter to be appended to the request
10244          * to defeat caching (defaults to {@link Ext.Updater.defaults#disableCaching}).</p></li></ul>
10245          * <p>
10246          * For example:
10247     <pre><code>
10248     um.update({
10249         url: "your-url.php",
10250         params: {param1: "foo", param2: "bar"}, // or a URL encoded string
10251         callback: yourFunction,
10252         scope: yourObject, //(optional scope)
10253         discardUrl: true,
10254         nocache: true,
10255         text: "Loading...",
10256         timeout: 60,
10257         scripts: false // Save time by avoiding RegExp execution.
10258     });
10259     </code></pre>
10260          */
10261         update : function(url, params, callback, discardUrl){
10262             var me = this,
10263                 cfg,
10264                 callerScope;
10265
10266             if(me.fireEvent(BEFOREUPDATE, me.el, url, params) !== false){
10267                 if(Ext.isObject(url)){ // must be config object
10268                     cfg = url;
10269                     url = cfg.url;
10270                     params = params || cfg.params;
10271                     callback = callback || cfg.callback;
10272                     discardUrl = discardUrl || cfg.discardUrl;
10273                     callerScope = cfg.scope;
10274                     if(!Ext.isEmpty(cfg.nocache)){me.disableCaching = cfg.nocache;};
10275                     if(!Ext.isEmpty(cfg.text)){me.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
10276                     if(!Ext.isEmpty(cfg.scripts)){me.loadScripts = cfg.scripts;};
10277                     if(!Ext.isEmpty(cfg.timeout)){me.timeout = cfg.timeout;};
10278                 }
10279                 me.showLoading();
10280
10281                 if(!discardUrl){
10282                     me.defaultUrl = url;
10283                 }
10284                 if(Ext.isFunction(url)){
10285                     url = url.call(me);
10286                 }
10287
10288                 var o = Ext.apply({}, {
10289                     url : url,
10290                     params: (Ext.isFunction(params) && callerScope) ? params.createDelegate(callerScope) : params,
10291                     success: processSuccess,
10292                     failure: processFailure,
10293                     scope: me,
10294                     callback: undefined,
10295                     timeout: (me.timeout*1000),
10296                     disableCaching: me.disableCaching,
10297                     argument: {
10298                         "options": cfg,
10299                         "url": url,
10300                         "form": null,
10301                         "callback": callback,
10302                         "scope": callerScope || window,
10303                         "params": params
10304                     }
10305                 }, cfg);
10306
10307                 me.transaction = Ext.Ajax.request(o);
10308             }
10309         },
10310
10311         /**
10312          * <p>Performs an asynchronous form post, updating this element with the response. If the form has the attribute
10313          * enctype="<a href="http://www.faqs.org/rfcs/rfc2388.html">multipart/form-data</a>", it assumes it's a file upload.
10314          * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.</p>
10315          * <p>File uploads are not performed using normal "Ajax" techniques, that is they are <b>not</b>
10316          * performed using XMLHttpRequests. Instead the form is submitted in the standard manner with the
10317          * DOM <code>&lt;form></code> element temporarily modified to have its
10318          * <a href="http://www.w3.org/TR/REC-html40/present/frames.html#adef-target">target</a> set to refer
10319          * to a dynamically generated, hidden <code>&lt;iframe></code> which is inserted into the document
10320          * but removed after the return data has been gathered.</p>
10321          * <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>
10322          * and some server technologies (notably JEE) may require some custom processing in order to
10323          * retrieve parameter names and parameter values from the packet content.</p>
10324          * @param {String/HTMLElement} form The form Id or form element
10325          * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
10326          * @param {Boolean} reset (optional) Whether to try to reset the form after the update
10327          * @param {Function} callback (optional) Callback when transaction is complete. The following
10328          * parameters are passed:<ul>
10329          * <li><b>el</b> : Ext.Element<p class="sub-desc">The Element being updated.</p></li>
10330          * <li><b>success</b> : Boolean<p class="sub-desc">True for success, false for failure.</p></li>
10331          * <li><b>response</b> : XMLHttpRequest<p class="sub-desc">The XMLHttpRequest which processed the update.</p></li></ul>
10332          */
10333         formUpdate : function(form, url, reset, callback){
10334             var me = this;
10335             if(me.fireEvent(BEFOREUPDATE, me.el, form, url) !== false){
10336                 if(Ext.isFunction(url)){
10337                     url = url.call(me);
10338                 }
10339                 form = Ext.getDom(form);
10340                 me.transaction = Ext.Ajax.request({
10341                     form: form,
10342                     url:url,
10343                     success: processSuccess,
10344                     failure: processFailure,
10345                     scope: me,
10346                     timeout: (me.timeout*1000),
10347                     argument: {
10348                         "url": url,
10349                         "form": form,
10350                         "callback": callback,
10351                         "reset": reset
10352                     }
10353                 });
10354                 me.showLoading.defer(1, me);
10355             }
10356         },
10357
10358         /**
10359          * Set this element to auto refresh.  Can be canceled by calling {@link #stopAutoRefresh}.
10360          * @param {Number} interval How often to update (in seconds).
10361          * @param {String/Object/Function} url (optional) The url for this request, a config object in the same format
10362          * supported by {@link #load}, or a function to call to get the url (defaults to the last used url).  Note that while
10363          * the url used in a load call can be reused by this method, other load config options will not be reused and must be
10364          * sepcified as part of a config object passed as this paramter if needed.
10365          * @param {String/Object} params (optional) The parameters to pass as either a url encoded string
10366          * "&param1=1&param2=2" or as an object {param1: 1, param2: 2}
10367          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
10368          * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
10369          */
10370         startAutoRefresh : function(interval, url, params, callback, refreshNow){
10371             var me = this;
10372             if(refreshNow){
10373                 me.update(url || me.defaultUrl, params, callback, true);
10374             }
10375             if(me.autoRefreshProcId){
10376                 clearInterval(me.autoRefreshProcId);
10377             }
10378             me.autoRefreshProcId = setInterval(me.update.createDelegate(me, [url || me.defaultUrl, params, callback, true]), interval * 1000);
10379         },
10380
10381         /**
10382          * Stop auto refresh on this element.
10383          */
10384         stopAutoRefresh : function(){
10385             if(this.autoRefreshProcId){
10386                 clearInterval(this.autoRefreshProcId);
10387                 delete this.autoRefreshProcId;
10388             }
10389         },
10390
10391         /**
10392          * Returns true if the Updater is currently set to auto refresh its content (see {@link #startAutoRefresh}), otherwise false.
10393          */
10394         isAutoRefreshing : function(){
10395            return !!this.autoRefreshProcId;
10396         },
10397
10398         /**
10399          * Display the element's "loading" state. By default, the element is updated with {@link #indicatorText}. This
10400          * method may be overridden to perform a custom action while this Updater is actively updating its contents.
10401          */
10402         showLoading : function(){
10403             if(this.showLoadIndicator){
10404                 this.el.dom.innerHTML = this.indicatorText;
10405             }
10406         },
10407
10408         /**
10409          * Aborts the currently executing transaction, if any.
10410          */
10411         abort : function(){
10412             if(this.transaction){
10413                 Ext.Ajax.abort(this.transaction);
10414             }
10415         },
10416
10417         /**
10418          * Returns true if an update is in progress, otherwise false.
10419          * @return {Boolean}
10420          */
10421         isUpdating : function(){
10422             return this.transaction ? Ext.Ajax.isLoading(this.transaction) : false;
10423         },
10424
10425         /**
10426          * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
10427          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
10428          */
10429         refresh : function(callback){
10430             if(this.defaultUrl){
10431                 this.update(this.defaultUrl, null, callback, true);
10432             }
10433         }
10434     }
10435 }());
10436
10437 /**
10438  * @class Ext.Updater.defaults
10439  * The defaults collection enables customizing the default properties of Updater
10440  */
10441 Ext.Updater.defaults = {
10442    /**
10443      * Timeout for requests or form posts in seconds (defaults to 30 seconds).
10444      * @type Number
10445      */
10446     timeout : 30,
10447     /**
10448      * True to append a unique parameter to GET requests to disable caching (defaults to false).
10449      * @type Boolean
10450      */
10451     disableCaching : false,
10452     /**
10453      * Whether or not to show {@link #indicatorText} during loading (defaults to true).
10454      * @type Boolean
10455      */
10456     showLoadIndicator : true,
10457     /**
10458      * Text for loading indicator (defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
10459      * @type String
10460      */
10461     indicatorText : '<div class="loading-indicator">Loading...</div>',
10462      /**
10463      * True to process scripts by default (defaults to false).
10464      * @type Boolean
10465      */
10466     loadScripts : false,
10467     /**
10468     * Blank page URL to use with SSL file uploads (defaults to {@link Ext#SSL_SECURE_URL} if set, or "javascript:false").
10469     * @type String
10470     */
10471     sslBlankUrl : Ext.SSL_SECURE_URL
10472 };
10473
10474
10475 /**
10476  * Static convenience method. <b>This method is deprecated in favor of el.load({url:'foo.php', ...})</b>.
10477  * Usage:
10478  * <pre><code>Ext.Updater.updateElement("my-div", "stuff.php");</code></pre>
10479  * @param {Mixed} el The element to update
10480  * @param {String} url The url
10481  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
10482  * @param {Object} options (optional) A config object with any of the Updater properties you want to set - for
10483  * example: {disableCaching:true, indicatorText: "Loading data..."}
10484  * @static
10485  * @deprecated
10486  * @member Ext.Updater
10487  */
10488 Ext.Updater.updateElement = function(el, url, params, options){
10489     var um = Ext.get(el).getUpdater();
10490     Ext.apply(um, options);
10491     um.update(url, params, options ? options.callback : null);
10492 };
10493
10494 /**
10495  * @class Ext.Updater.BasicRenderer
10496  * <p>This class is a base class implementing a simple render method which updates an element using results from an Ajax request.</p>
10497  * <p>The BasicRenderer updates the element's innerHTML with the responseText. To perform a custom render (i.e. XML or JSON processing),
10498  * create an object with a conforming {@link #render} method and pass it to setRenderer on the Updater.</p>
10499  */
10500 Ext.Updater.BasicRenderer = function(){};
10501
10502 Ext.Updater.BasicRenderer.prototype = {
10503     /**
10504      * This method is called when an Ajax response is received, and an Element needs updating.
10505      * @param {Ext.Element} el The element being rendered
10506      * @param {Object} xhr The XMLHttpRequest object
10507      * @param {Updater} updateManager The calling update manager
10508      * @param {Function} callback A callback that will need to be called if loadScripts is true on the Updater
10509      */
10510      render : function(el, response, updateManager, callback){
10511         el.update(response.responseText, updateManager.loadScripts, callback);
10512     }
10513 };/**
10514  * @class Date
10515  *
10516  * The date parsing and formatting syntax contains a subset of
10517  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
10518  * supported will provide results equivalent to their PHP versions.
10519  *
10520  * The following is a list of all currently supported formats:
10521  * <pre>
10522 Format  Description                                                               Example returned values
10523 ------  -----------------------------------------------------------------------   -----------------------
10524   d     Day of the month, 2 digits with leading zeros                             01 to 31
10525   D     A short textual representation of the day of the week                     Mon to Sun
10526   j     Day of the month without leading zeros                                    1 to 31
10527   l     A full textual representation of the day of the week                      Sunday to Saturday
10528   N     ISO-8601 numeric representation of the day of the week                    1 (for Monday) through 7 (for Sunday)
10529   S     English ordinal suffix for the day of the month, 2 characters             st, nd, rd or th. Works well with j
10530   w     Numeric representation of the day of the week                             0 (for Sunday) to 6 (for Saturday)
10531   z     The day of the year (starting from 0)                                     0 to 364 (365 in leap years)
10532   W     ISO-8601 week number of year, weeks starting on Monday                    01 to 53
10533   F     A full textual representation of a month, such as January or March        January to December
10534   m     Numeric representation of a month, with leading zeros                     01 to 12
10535   M     A short textual representation of a month                                 Jan to Dec
10536   n     Numeric representation of a month, without leading zeros                  1 to 12
10537   t     Number of days in the given month                                         28 to 31
10538   L     Whether it's a leap year                                                  1 if it is a leap year, 0 otherwise.
10539   o     ISO-8601 year number (identical to (Y), but if the ISO week number (W)    Examples: 1998 or 2004
10540         belongs to the previous or next year, that year is used instead)
10541   Y     A full numeric representation of a year, 4 digits                         Examples: 1999 or 2003
10542   y     A two digit representation of a year                                      Examples: 99 or 03
10543   a     Lowercase Ante meridiem and Post meridiem                                 am or pm
10544   A     Uppercase Ante meridiem and Post meridiem                                 AM or PM
10545   g     12-hour format of an hour without leading zeros                           1 to 12
10546   G     24-hour format of an hour without leading zeros                           0 to 23
10547   h     12-hour format of an hour with leading zeros                              01 to 12
10548   H     24-hour format of an hour with leading zeros                              00 to 23
10549   i     Minutes, with leading zeros                                               00 to 59
10550   s     Seconds, with leading zeros                                               00 to 59
10551   u     Decimal fraction of a second                                              Examples:
10552         (minimum 1 digit, arbitrary number of digits allowed)                     001 (i.e. 0.001s) or
10553                                                                                   100 (i.e. 0.100s) or
10554                                                                                   999 (i.e. 0.999s) or
10555                                                                                   999876543210 (i.e. 0.999876543210s)
10556   O     Difference to Greenwich time (GMT) in hours and minutes                   Example: +1030
10557   P     Difference to Greenwich time (GMT) with colon between hours and minutes   Example: -08:00
10558   T     Timezone abbreviation of the machine running the code                     Examples: EST, MDT, PDT ...
10559   Z     Timezone offset in seconds (negative if west of UTC, positive if east)    -43200 to 50400
10560   c     ISO 8601 date
10561         Notes:                                                                    Examples:
10562         1) If unspecified, the month / day defaults to the current month / day,   1991 or
10563            the time defaults to midnight, while the timezone defaults to the      1992-10 or
10564            browser's timezone. If a time is specified, it must include both hours 1993-09-20 or
10565            and minutes. The "T" delimiter, seconds, milliseconds and timezone     1994-08-19T16:20+01:00 or
10566            are optional.                                                          1995-07-18T17:21:28-02:00 or
10567         2) The decimal fraction of a second, if specified, must contain at        1996-06-17T18:22:29.98765+03:00 or
10568            least 1 digit (there is no limit to the maximum number                 1997-05-16T19:23:30,12345-0400 or
10569            of digits allowed), and may be delimited by either a '.' or a ','      1998-04-15T20:24:31.2468Z or
10570         Refer to the examples on the right for the various levels of              1999-03-14T20:24:32Z or
10571         date-time granularity which are supported, or see                         2000-02-13T21:25:33
10572         http://www.w3.org/TR/NOTE-datetime for more info.                         2001-01-12 22:26:34
10573   U     Seconds since the Unix Epoch (January 1 1970 00:00:00 GMT)                1193432466 or -2138434463
10574   M$    Microsoft AJAX serialized dates                                           \/Date(1238606590509)\/ (i.e. UTC milliseconds since epoch) or
10575                                                                                   \/Date(1238606590509+0800)\/
10576 </pre>
10577  *
10578  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
10579  * <pre><code>
10580 // Sample date:
10581 // 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
10582
10583 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
10584 document.write(dt.format('Y-m-d'));                           // 2007-01-10
10585 document.write(dt.format('F j, Y, g:i a'));                   // January 10, 2007, 3:05 pm
10586 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
10587 </code></pre>
10588  *
10589  * Here are some standard date/time patterns that you might find helpful.  They
10590  * are not part of the source of Date.js, but to use them you can simply copy this
10591  * block of code into any script that is included after Date.js and they will also become
10592  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
10593  * <pre><code>
10594 Date.patterns = {
10595     ISO8601Long:"Y-m-d H:i:s",
10596     ISO8601Short:"Y-m-d",
10597     ShortDate: "n/j/Y",
10598     LongDate: "l, F d, Y",
10599     FullDateTime: "l, F d, Y g:i:s A",
10600     MonthDay: "F d",
10601     ShortTime: "g:i A",
10602     LongTime: "g:i:s A",
10603     SortableDateTime: "Y-m-d\\TH:i:s",
10604     UniversalSortableDateTime: "Y-m-d H:i:sO",
10605     YearMonth: "F, Y"
10606 };
10607 </code></pre>
10608  *
10609  * Example usage:
10610  * <pre><code>
10611 var dt = new Date();
10612 document.write(dt.format(Date.patterns.ShortDate));
10613 </code></pre>
10614  * <p>Developer-written, custom formats may be used by supplying both a formatting and a parsing function
10615  * which perform to specialized requirements. The functions are stored in {@link #parseFunctions} and {@link #formatFunctions}.</p>
10616  */
10617
10618 /*
10619  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
10620  * (see http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/)
10621  * They generate precompiled functions from format patterns instead of parsing and
10622  * processing each pattern every time a date is formatted. These functions are available
10623  * on every Date object.
10624  */
10625
10626 (function() {
10627
10628 /**
10629  * Global flag which determines if strict date parsing should be used.
10630  * Strict date parsing will not roll-over invalid dates, which is the
10631  * default behaviour of javascript Date objects.
10632  * (see {@link #parseDate} for more information)
10633  * Defaults to <tt>false</tt>.
10634  * @static
10635  * @type Boolean
10636 */
10637 Date.useStrict = false;
10638
10639
10640 // create private copy of Ext's String.format() method
10641 // - to remove unnecessary dependency
10642 // - to resolve namespace conflict with M$-Ajax's implementation
10643 function xf(format) {
10644     var args = Array.prototype.slice.call(arguments, 1);
10645     return format.replace(/\{(\d+)\}/g, function(m, i) {
10646         return args[i];
10647     });
10648 }
10649
10650
10651 // private
10652 Date.formatCodeToRegex = function(character, currentGroup) {
10653     // Note: currentGroup - position in regex result array (see notes for Date.parseCodes below)
10654     var p = Date.parseCodes[character];
10655
10656     if (p) {
10657       p = typeof p == 'function'? p() : p;
10658       Date.parseCodes[character] = p; // reassign function result to prevent repeated execution
10659     }
10660
10661     return p ? Ext.applyIf({
10662       c: p.c ? xf(p.c, currentGroup || "{0}") : p.c
10663     }, p) : {
10664         g:0,
10665         c:null,
10666         s:Ext.escapeRe(character) // treat unrecognised characters as literals
10667     }
10668 };
10669
10670 // private shorthand for Date.formatCodeToRegex since we'll be using it fairly often
10671 var $f = Date.formatCodeToRegex;
10672
10673 Ext.apply(Date, {
10674     /**
10675      * <p>An object hash in which each property is a date parsing function. The property name is the
10676      * format string which that function parses.</p>
10677      * <p>This object is automatically populated with date parsing functions as
10678      * date formats are requested for Ext standard formatting strings.</p>
10679      * <p>Custom parsing functions may be inserted into this object, keyed by a name which from then on
10680      * may be used as a format string to {@link #parseDate}.<p>
10681      * <p>Example:</p><pre><code>
10682 Date.parseFunctions['x-date-format'] = myDateParser;
10683 </code></pre>
10684      * <p>A parsing function should return a Date object, and is passed the following parameters:<div class="mdetail-params"><ul>
10685      * <li><code>date</code> : String<div class="sub-desc">The date string to parse.</div></li>
10686      * <li><code>strict</code> : Boolean<div class="sub-desc">True to validate date strings while parsing
10687      * (i.e. prevent javascript Date "rollover") (The default must be false).
10688      * Invalid date strings should return null when parsed.</div></li>
10689      * </ul></div></p>
10690      * <p>To enable Dates to also be <i>formatted</i> according to that format, a corresponding
10691      * formatting function must be placed into the {@link #formatFunctions} property.
10692      * @property parseFunctions
10693      * @static
10694      * @type Object
10695      */
10696     parseFunctions: {
10697         "M$": function(input, strict) {
10698             // note: the timezone offset is ignored since the M$ Ajax server sends
10699             // a UTC milliseconds-since-Unix-epoch value (negative values are allowed)
10700             var re = new RegExp('\\/Date\\(([-+])?(\\d+)(?:[+-]\\d{4})?\\)\\/');
10701             var r = (input || '').match(re);
10702             return r? new Date(((r[1] || '') + r[2]) * 1) : null;
10703         }
10704     },
10705     parseRegexes: [],
10706
10707     /**
10708      * <p>An object hash in which each property is a date formatting function. The property name is the
10709      * format string which corresponds to the produced formatted date string.</p>
10710      * <p>This object is automatically populated with date formatting functions as
10711      * date formats are requested for Ext standard formatting strings.</p>
10712      * <p>Custom formatting functions may be inserted into this object, keyed by a name which from then on
10713      * may be used as a format string to {@link #format}. Example:</p><pre><code>
10714 Date.formatFunctions['x-date-format'] = myDateFormatter;
10715 </code></pre>
10716      * <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>
10717      * <li><code>date</code> : Date<div class="sub-desc">The Date to format.</div></li>
10718      * </ul></div></p>
10719      * <p>To enable date strings to also be <i>parsed</i> according to that format, a corresponding
10720      * parsing function must be placed into the {@link #parseFunctions} property.
10721      * @property formatFunctions
10722      * @static
10723      * @type Object
10724      */
10725     formatFunctions: {
10726         "M$": function() {
10727             // UTC milliseconds since Unix epoch (M$-AJAX serialized date format (MRSF))
10728             return '\\/Date(' + this.getTime() + ')\\/';
10729         }
10730     },
10731
10732     y2kYear : 50,
10733
10734     /**
10735      * Date interval constant
10736      * @static
10737      * @type String
10738      */
10739     MILLI : "ms",
10740
10741     /**
10742      * Date interval constant
10743      * @static
10744      * @type String
10745      */
10746     SECOND : "s",
10747
10748     /**
10749      * Date interval constant
10750      * @static
10751      * @type String
10752      */
10753     MINUTE : "mi",
10754
10755     /** Date interval constant
10756      * @static
10757      * @type String
10758      */
10759     HOUR : "h",
10760
10761     /**
10762      * Date interval constant
10763      * @static
10764      * @type String
10765      */
10766     DAY : "d",
10767
10768     /**
10769      * Date interval constant
10770      * @static
10771      * @type String
10772      */
10773     MONTH : "mo",
10774
10775     /**
10776      * Date interval constant
10777      * @static
10778      * @type String
10779      */
10780     YEAR : "y",
10781
10782     /**
10783      * <p>An object hash containing default date values used during date parsing.</p>
10784      * <p>The following properties are available:<div class="mdetail-params"><ul>
10785      * <li><code>y</code> : Number<div class="sub-desc">The default year value. (defaults to undefined)</div></li>
10786      * <li><code>m</code> : Number<div class="sub-desc">The default 1-based month value. (defaults to undefined)</div></li>
10787      * <li><code>d</code> : Number<div class="sub-desc">The default day value. (defaults to undefined)</div></li>
10788      * <li><code>h</code> : Number<div class="sub-desc">The default hour value. (defaults to undefined)</div></li>
10789      * <li><code>i</code> : Number<div class="sub-desc">The default minute value. (defaults to undefined)</div></li>
10790      * <li><code>s</code> : Number<div class="sub-desc">The default second value. (defaults to undefined)</div></li>
10791      * <li><code>ms</code> : Number<div class="sub-desc">The default millisecond value. (defaults to undefined)</div></li>
10792      * </ul></div></p>
10793      * <p>Override these properties to customize the default date values used by the {@link #parseDate} method.</p>
10794      * <p><b>Note: In countries which experience Daylight Saving Time (i.e. DST), the <tt>h</tt>, <tt>i</tt>, <tt>s</tt>
10795      * and <tt>ms</tt> properties may coincide with the exact time in which DST takes effect.
10796      * It is the responsiblity of the developer to account for this.</b></p>
10797      * Example Usage:
10798      * <pre><code>
10799 // set default day value to the first day of the month
10800 Date.defaults.d = 1;
10801
10802 // parse a February date string containing only year and month values.
10803 // setting the default day value to 1 prevents weird date rollover issues
10804 // when attempting to parse the following date string on, for example, March 31st 2009.
10805 Date.parseDate('2009-02', 'Y-m'); // returns a Date object representing February 1st 2009
10806 </code></pre>
10807      * @property defaults
10808      * @static
10809      * @type Object
10810      */
10811     defaults: {},
10812
10813     /**
10814      * An array of textual day names.
10815      * Override these values for international dates.
10816      * Example:
10817      * <pre><code>
10818 Date.dayNames = [
10819     'SundayInYourLang',
10820     'MondayInYourLang',
10821     ...
10822 ];
10823 </code></pre>
10824      * @type Array
10825      * @static
10826      */
10827     dayNames : [
10828         "Sunday",
10829         "Monday",
10830         "Tuesday",
10831         "Wednesday",
10832         "Thursday",
10833         "Friday",
10834         "Saturday"
10835     ],
10836
10837     /**
10838      * An array of textual month names.
10839      * Override these values for international dates.
10840      * Example:
10841      * <pre><code>
10842 Date.monthNames = [
10843     'JanInYourLang',
10844     'FebInYourLang',
10845     ...
10846 ];
10847 </code></pre>
10848      * @type Array
10849      * @static
10850      */
10851     monthNames : [
10852         "January",
10853         "February",
10854         "March",
10855         "April",
10856         "May",
10857         "June",
10858         "July",
10859         "August",
10860         "September",
10861         "October",
10862         "November",
10863         "December"
10864     ],
10865
10866     /**
10867      * An object hash of zero-based javascript month numbers (with short month names as keys. note: keys are case-sensitive).
10868      * Override these values for international dates.
10869      * Example:
10870      * <pre><code>
10871 Date.monthNumbers = {
10872     'ShortJanNameInYourLang':0,
10873     'ShortFebNameInYourLang':1,
10874     ...
10875 };
10876 </code></pre>
10877      * @type Object
10878      * @static
10879      */
10880     monthNumbers : {
10881         Jan:0,
10882         Feb:1,
10883         Mar:2,
10884         Apr:3,
10885         May:4,
10886         Jun:5,
10887         Jul:6,
10888         Aug:7,
10889         Sep:8,
10890         Oct:9,
10891         Nov:10,
10892         Dec:11
10893     },
10894
10895     /**
10896      * Get the short month name for the given month number.
10897      * Override this function for international dates.
10898      * @param {Number} month A zero-based javascript month number.
10899      * @return {String} The short month name.
10900      * @static
10901      */
10902     getShortMonthName : function(month) {
10903         return Date.monthNames[month].substring(0, 3);
10904     },
10905
10906     /**
10907      * Get the short day name for the given day number.
10908      * Override this function for international dates.
10909      * @param {Number} day A zero-based javascript day number.
10910      * @return {String} The short day name.
10911      * @static
10912      */
10913     getShortDayName : function(day) {
10914         return Date.dayNames[day].substring(0, 3);
10915     },
10916
10917     /**
10918      * Get the zero-based javascript month number for the given short/full month name.
10919      * Override this function for international dates.
10920      * @param {String} name The short/full month name.
10921      * @return {Number} The zero-based javascript month number.
10922      * @static
10923      */
10924     getMonthNumber : function(name) {
10925         // handle camel casing for english month names (since the keys for the Date.monthNumbers hash are case sensitive)
10926         return Date.monthNumbers[name.substring(0, 1).toUpperCase() + name.substring(1, 3).toLowerCase()];
10927     },
10928
10929     /**
10930      * The base format-code to formatting-function hashmap used by the {@link #format} method.
10931      * Formatting functions are strings (or functions which return strings) which
10932      * will return the appropriate value when evaluated in the context of the Date object
10933      * from which the {@link #format} method is called.
10934      * Add to / override these mappings for custom date formatting.
10935      * Note: Date.format() treats characters as literals if an appropriate mapping cannot be found.
10936      * Example:
10937      * <pre><code>
10938 Date.formatCodes.x = "String.leftPad(this.getDate(), 2, '0')";
10939 (new Date()).format("X"); // returns the current day of the month
10940 </code></pre>
10941      * @type Object
10942      * @static
10943      */
10944     formatCodes : {
10945         d: "String.leftPad(this.getDate(), 2, '0')",
10946         D: "Date.getShortDayName(this.getDay())", // get localised short day name
10947         j: "this.getDate()",
10948         l: "Date.dayNames[this.getDay()]",
10949         N: "(this.getDay() ? this.getDay() : 7)",
10950         S: "this.getSuffix()",
10951         w: "this.getDay()",
10952         z: "this.getDayOfYear()",
10953         W: "String.leftPad(this.getWeekOfYear(), 2, '0')",
10954         F: "Date.monthNames[this.getMonth()]",
10955         m: "String.leftPad(this.getMonth() + 1, 2, '0')",
10956         M: "Date.getShortMonthName(this.getMonth())", // get localised short month name
10957         n: "(this.getMonth() + 1)",
10958         t: "this.getDaysInMonth()",
10959         L: "(this.isLeapYear() ? 1 : 0)",
10960         o: "(this.getFullYear() + (this.getWeekOfYear() == 1 && this.getMonth() > 0 ? +1 : (this.getWeekOfYear() >= 52 && this.getMonth() < 11 ? -1 : 0)))",
10961         Y: "this.getFullYear()",
10962         y: "('' + this.getFullYear()).substring(2, 4)",
10963         a: "(this.getHours() < 12 ? 'am' : 'pm')",
10964         A: "(this.getHours() < 12 ? 'AM' : 'PM')",
10965         g: "((this.getHours() % 12) ? this.getHours() % 12 : 12)",
10966         G: "this.getHours()",
10967         h: "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0')",
10968         H: "String.leftPad(this.getHours(), 2, '0')",
10969         i: "String.leftPad(this.getMinutes(), 2, '0')",
10970         s: "String.leftPad(this.getSeconds(), 2, '0')",
10971         u: "String.leftPad(this.getMilliseconds(), 3, '0')",
10972         O: "this.getGMTOffset()",
10973         P: "this.getGMTOffset(true)",
10974         T: "this.getTimezone()",
10975         Z: "(this.getTimezoneOffset() * -60)",
10976
10977         c: function() { // ISO-8601 -- GMT format
10978             for (var c = "Y-m-dTH:i:sP", code = [], i = 0, l = c.length; i < l; ++i) {
10979                 var e = c.charAt(i);
10980                 code.push(e == "T" ? "'T'" : Date.getFormatCode(e)); // treat T as a character literal
10981             }
10982             return code.join(" + ");
10983         },
10984         /*
10985         c: function() { // ISO-8601 -- UTC format
10986             return [
10987               "this.getUTCFullYear()", "'-'",
10988               "String.leftPad(this.getUTCMonth() + 1, 2, '0')", "'-'",
10989               "String.leftPad(this.getUTCDate(), 2, '0')",
10990               "'T'",
10991               "String.leftPad(this.getUTCHours(), 2, '0')", "':'",
10992               "String.leftPad(this.getUTCMinutes(), 2, '0')", "':'",
10993               "String.leftPad(this.getUTCSeconds(), 2, '0')",
10994               "'Z'"
10995             ].join(" + ");
10996         },
10997         */
10998
10999         U: "Math.round(this.getTime() / 1000)"
11000     },
11001
11002     /**
11003      * Checks if the passed Date parameters will cause a javascript Date "rollover".
11004      * @param {Number} year 4-digit year
11005      * @param {Number} month 1-based month-of-year
11006      * @param {Number} day Day of month
11007      * @param {Number} hour (optional) Hour
11008      * @param {Number} minute (optional) Minute
11009      * @param {Number} second (optional) Second
11010      * @param {Number} millisecond (optional) Millisecond
11011      * @return {Boolean} true if the passed parameters do not cause a Date "rollover", false otherwise.
11012      * @static
11013      */
11014     isValid : function(y, m, d, h, i, s, ms) {
11015         // setup defaults
11016         h = h || 0;
11017         i = i || 0;
11018         s = s || 0;
11019         ms = ms || 0;
11020
11021         var dt = new Date(y, m - 1, d, h, i, s, ms);
11022
11023         return y == dt.getFullYear() &&
11024             m == dt.getMonth() + 1 &&
11025             d == dt.getDate() &&
11026             h == dt.getHours() &&
11027             i == dt.getMinutes() &&
11028             s == dt.getSeconds() &&
11029             ms == dt.getMilliseconds();
11030     },
11031
11032     /**
11033      * Parses the passed string using the specified date format.
11034      * Note that this function expects normal calendar dates, meaning that months are 1-based (i.e. 1 = January).
11035      * The {@link #defaults} hash will be used for any date value (i.e. year, month, day, hour, minute, second or millisecond)
11036      * which cannot be found in the passed string. If a corresponding default date value has not been specified in the {@link #defaults} hash,
11037      * the current date's year, month, day or DST-adjusted zero-hour time value will be used instead.
11038      * Keep in mind that the input date string must precisely match the specified format string
11039      * in order for the parse operation to be successful (failed parse operations return a null value).
11040      * <p>Example:</p><pre><code>
11041 //dt = Fri May 25 2007 (current date)
11042 var dt = new Date();
11043
11044 //dt = Thu May 25 2006 (today&#39;s month/day in 2006)
11045 dt = Date.parseDate("2006", "Y");
11046
11047 //dt = Sun Jan 15 2006 (all date parts specified)
11048 dt = Date.parseDate("2006-01-15", "Y-m-d");
11049
11050 //dt = Sun Jan 15 2006 15:20:01
11051 dt = Date.parseDate("2006-01-15 3:20:01 PM", "Y-m-d g:i:s A");
11052
11053 // attempt to parse Sun Feb 29 2006 03:20:01 in strict mode
11054 dt = Date.parseDate("2006-02-29 03:20:01", "Y-m-d H:i:s", true); // returns null
11055 </code></pre>
11056      * @param {String} input The raw date string.
11057      * @param {String} format The expected date string format.
11058      * @param {Boolean} strict (optional) True to validate date strings while parsing (i.e. prevents javascript Date "rollover")
11059                         (defaults to false). Invalid date strings will return null when parsed.
11060      * @return {Date} The parsed Date.
11061      * @static
11062      */
11063     parseDate : function(input, format, strict) {
11064         var p = Date.parseFunctions;
11065         if (p[format] == null) {
11066             Date.createParser(format);
11067         }
11068         return p[format](input, Ext.isDefined(strict) ? strict : Date.useStrict);
11069     },
11070
11071     // private
11072     getFormatCode : function(character) {
11073         var f = Date.formatCodes[character];
11074
11075         if (f) {
11076           f = typeof f == 'function'? f() : f;
11077           Date.formatCodes[character] = f; // reassign function result to prevent repeated execution
11078         }
11079
11080         // note: unknown characters are treated as literals
11081         return f || ("'" + String.escape(character) + "'");
11082     },
11083
11084     // private
11085     createFormat : function(format) {
11086         var code = [],
11087             special = false,
11088             ch = '';
11089
11090         for (var i = 0; i < format.length; ++i) {
11091             ch = format.charAt(i);
11092             if (!special && ch == "\\") {
11093                 special = true;
11094             } else if (special) {
11095                 special = false;
11096                 code.push("'" + String.escape(ch) + "'");
11097             } else {
11098                 code.push(Date.getFormatCode(ch))
11099             }
11100         }
11101         Date.formatFunctions[format] = new Function("return " + code.join('+'));
11102     },
11103
11104     // private
11105     createParser : function() {
11106         var code = [
11107             "var dt, y, m, d, h, i, s, ms, o, z, zz, u, v,",
11108                 "def = Date.defaults,",
11109                 "results = String(input).match(Date.parseRegexes[{0}]);", // either null, or an array of matched strings
11110
11111             "if(results){",
11112                 "{1}",
11113
11114                 "if(u != null){", // i.e. unix time is defined
11115                     "v = new Date(u * 1000);", // give top priority to UNIX time
11116                 "}else{",
11117                     // create Date object representing midnight of the current day;
11118                     // this will provide us with our date defaults
11119                     // (note: clearTime() handles Daylight Saving Time automatically)
11120                     "dt = (new Date()).clearTime();",
11121
11122                     // date calculations (note: these calculations create a dependency on Ext.num())
11123                     "y = Ext.num(y, Ext.num(def.y, dt.getFullYear()));",
11124                     "m = Ext.num(m, Ext.num(def.m - 1, dt.getMonth()));",
11125                     "d = Ext.num(d, Ext.num(def.d, dt.getDate()));",
11126
11127                     // time calculations (note: these calculations create a dependency on Ext.num())
11128                     "h  = Ext.num(h, Ext.num(def.h, dt.getHours()));",
11129                     "i  = Ext.num(i, Ext.num(def.i, dt.getMinutes()));",
11130                     "s  = Ext.num(s, Ext.num(def.s, dt.getSeconds()));",
11131                     "ms = Ext.num(ms, Ext.num(def.ms, dt.getMilliseconds()));",
11132
11133                     "if(z >= 0 && y >= 0){",
11134                         // both the year and zero-based day of year are defined and >= 0.
11135                         // these 2 values alone provide sufficient info to create a full date object
11136
11137                         // create Date object representing January 1st for the given year
11138                         "v = new Date(y, 0, 1, h, i, s, ms);",
11139
11140                         // then add day of year, checking for Date "rollover" if necessary
11141                         "v = !strict? v : (strict === true && (z <= 364 || (v.isLeapYear() && z <= 365))? v.add(Date.DAY, z) : null);",
11142                     "}else if(strict === true && !Date.isValid(y, m + 1, d, h, i, s, ms)){", // check for Date "rollover"
11143                         "v = null;", // invalid date, so return null
11144                     "}else{",
11145                         // plain old Date object
11146                         "v = new Date(y, m, d, h, i, s, ms);",
11147                     "}",
11148                 "}",
11149             "}",
11150
11151             "if(v){",
11152                 // favour UTC offset over GMT offset
11153                 "if(zz != null){",
11154                     // reset to UTC, then add offset
11155                     "v = v.add(Date.SECOND, -v.getTimezoneOffset() * 60 - zz);",
11156                 "}else if(o){",
11157                     // reset to GMT, then add offset
11158                     "v = v.add(Date.MINUTE, -v.getTimezoneOffset() + (sn == '+'? -1 : 1) * (hr * 60 + mn));",
11159                 "}",
11160             "}",
11161
11162             "return v;"
11163         ].join('\n');
11164
11165         return function(format) {
11166             var regexNum = Date.parseRegexes.length,
11167                 currentGroup = 1,
11168                 calc = [],
11169                 regex = [],
11170                 special = false,
11171                 ch = "";
11172
11173             for (var i = 0; i < format.length; ++i) {
11174                 ch = format.charAt(i);
11175                 if (!special && ch == "\\") {
11176                     special = true;
11177                 } else if (special) {
11178                     special = false;
11179                     regex.push(String.escape(ch));
11180                 } else {
11181                     var obj = $f(ch, currentGroup);
11182                     currentGroup += obj.g;
11183                     regex.push(obj.s);
11184                     if (obj.g && obj.c) {
11185                         calc.push(obj.c);
11186                     }
11187                 }
11188             }
11189
11190             Date.parseRegexes[regexNum] = new RegExp("^" + regex.join('') + "$");
11191             Date.parseFunctions[format] = new Function("input", "strict", xf(code, regexNum, calc.join('')));
11192         }
11193     }(),
11194
11195     // private
11196     parseCodes : {
11197         /*
11198          * Notes:
11199          * g = {Number} calculation group (0 or 1. only group 1 contributes to date calculations.)
11200          * c = {String} calculation method (required for group 1. null for group 0. {0} = currentGroup - position in regex result array)
11201          * s = {String} regex pattern. all matches are stored in results[], and are accessible by the calculation mapped to 'c'
11202          */
11203         d: {
11204             g:1,
11205             c:"d = parseInt(results[{0}], 10);\n",
11206             s:"(\\d{2})" // day of month with leading zeroes (01 - 31)
11207         },
11208         j: {
11209             g:1,
11210             c:"d = parseInt(results[{0}], 10);\n",
11211             s:"(\\d{1,2})" // day of month without leading zeroes (1 - 31)
11212         },
11213         D: function() {
11214             for (var a = [], i = 0; i < 7; a.push(Date.getShortDayName(i)), ++i); // get localised short day names
11215             return {
11216                 g:0,
11217                 c:null,
11218                 s:"(?:" + a.join("|") +")"
11219             }
11220         },
11221         l: function() {
11222             return {
11223                 g:0,
11224                 c:null,
11225                 s:"(?:" + Date.dayNames.join("|") + ")"
11226             }
11227         },
11228         N: {
11229             g:0,
11230             c:null,
11231             s:"[1-7]" // ISO-8601 day number (1 (monday) - 7 (sunday))
11232         },
11233         S: {
11234             g:0,
11235             c:null,
11236             s:"(?:st|nd|rd|th)"
11237         },
11238         w: {
11239             g:0,
11240             c:null,
11241             s:"[0-6]" // javascript day number (0 (sunday) - 6 (saturday))
11242         },
11243         z: {
11244             g:1,
11245             c:"z = parseInt(results[{0}], 10);\n",
11246             s:"(\\d{1,3})" // day of the year (0 - 364 (365 in leap years))
11247         },
11248         W: {
11249             g:0,
11250             c:null,
11251             s:"(?:\\d{2})" // ISO-8601 week number (with leading zero)
11252         },
11253         F: function() {
11254             return {
11255                 g:1,
11256                 c:"m = parseInt(Date.getMonthNumber(results[{0}]), 10);\n", // get localised month number
11257                 s:"(" + Date.monthNames.join("|") + ")"
11258             }
11259         },
11260         M: function() {
11261             for (var a = [], i = 0; i < 12; a.push(Date.getShortMonthName(i)), ++i); // get localised short month names
11262             return Ext.applyIf({
11263                 s:"(" + a.join("|") + ")"
11264             }, $f("F"));
11265         },
11266         m: {
11267             g:1,
11268             c:"m = parseInt(results[{0}], 10) - 1;\n",
11269             s:"(\\d{2})" // month number with leading zeros (01 - 12)
11270         },
11271         n: {
11272             g:1,
11273             c:"m = parseInt(results[{0}], 10) - 1;\n",
11274             s:"(\\d{1,2})" // month number without leading zeros (1 - 12)
11275         },
11276         t: {
11277             g:0,
11278             c:null,
11279             s:"(?:\\d{2})" // no. of days in the month (28 - 31)
11280         },
11281         L: {
11282             g:0,
11283             c:null,
11284             s:"(?:1|0)"
11285         },
11286         o: function() {
11287             return $f("Y");
11288         },
11289         Y: {
11290             g:1,
11291             c:"y = parseInt(results[{0}], 10);\n",
11292             s:"(\\d{4})" // 4-digit year
11293         },
11294         y: {
11295             g:1,
11296             c:"var ty = parseInt(results[{0}], 10);\n"
11297                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n", // 2-digit year
11298             s:"(\\d{1,2})"
11299         },
11300         a: {
11301             g:1,
11302             c:"if (results[{0}] == 'am') {\n"
11303                 + "if (!h || h == 12) { h = 0; }\n"
11304                 + "} else { if (!h || h < 12) { h = (h || 0) + 12; }}",
11305             s:"(am|pm)"
11306         },
11307         A: {
11308             g:1,
11309             c:"if (results[{0}] == 'AM') {\n"
11310                 + "if (!h || h == 12) { h = 0; }\n"
11311                 + "} else { if (!h || h < 12) { h = (h || 0) + 12; }}",
11312             s:"(AM|PM)"
11313         },
11314         g: function() {
11315             return $f("G");
11316         },
11317         G: {
11318             g:1,
11319             c:"h = parseInt(results[{0}], 10);\n",
11320             s:"(\\d{1,2})" // 24-hr format of an hour without leading zeroes (0 - 23)
11321         },
11322         h: function() {
11323             return $f("H");
11324         },
11325         H: {
11326             g:1,
11327             c:"h = parseInt(results[{0}], 10);\n",
11328             s:"(\\d{2})" //  24-hr format of an hour with leading zeroes (00 - 23)
11329         },
11330         i: {
11331             g:1,
11332             c:"i = parseInt(results[{0}], 10);\n",
11333             s:"(\\d{2})" // minutes with leading zeros (00 - 59)
11334         },
11335         s: {
11336             g:1,
11337             c:"s = parseInt(results[{0}], 10);\n",
11338             s:"(\\d{2})" // seconds with leading zeros (00 - 59)
11339         },
11340         u: {
11341             g:1,
11342             c:"ms = results[{0}]; ms = parseInt(ms, 10)/Math.pow(10, ms.length - 3);\n",
11343             s:"(\\d+)" // decimal fraction of a second (minimum = 1 digit, maximum = unlimited)
11344         },
11345         O: {
11346             g:1,
11347             c:[
11348                 "o = results[{0}];",
11349                 "var sn = o.substring(0,1),", // get + / - sign
11350                     "hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60),", // get hours (performs minutes-to-hour conversion also, just in case)
11351                     "mn = o.substring(3,5) % 60;", // get minutes
11352                 "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
11353             ].join("\n"),
11354             s: "([+\-]\\d{4})" // GMT offset in hrs and mins
11355         },
11356         P: {
11357             g:1,
11358             c:[
11359                 "o = results[{0}];",
11360                 "var sn = o.substring(0,1),", // get + / - sign
11361                     "hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60),", // get hours (performs minutes-to-hour conversion also, just in case)
11362                     "mn = o.substring(4,6) % 60;", // get minutes
11363                 "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
11364             ].join("\n"),
11365             s: "([+\-]\\d{2}:\\d{2})" // GMT offset in hrs and mins (with colon separator)
11366         },
11367         T: {
11368             g:0,
11369             c:null,
11370             s:"[A-Z]{1,4}" // timezone abbrev. may be between 1 - 4 chars
11371         },
11372         Z: {
11373             g:1,
11374             c:"zz = results[{0}] * 1;\n" // -43200 <= UTC offset <= 50400
11375                   + "zz = (-43200 <= zz && zz <= 50400)? zz : null;\n",
11376             s:"([+\-]?\\d{1,5})" // leading '+' sign is optional for UTC offset
11377         },
11378         c: function() {
11379             var calc = [],
11380                 arr = [
11381                     $f("Y", 1), // year
11382                     $f("m", 2), // month
11383                     $f("d", 3), // day
11384                     $f("h", 4), // hour
11385                     $f("i", 5), // minute
11386                     $f("s", 6), // second
11387                     {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)
11388                     {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
11389                         "if(results[8]) {", // timezone specified
11390                             "if(results[8] == 'Z'){",
11391                                 "zz = 0;", // UTC
11392                             "}else if (results[8].indexOf(':') > -1){",
11393                                 $f("P", 8).c, // timezone offset with colon separator
11394                             "}else{",
11395                                 $f("O", 8).c, // timezone offset without colon separator
11396                             "}",
11397                         "}"
11398                     ].join('\n')}
11399                 ];
11400
11401             for (var i = 0, l = arr.length; i < l; ++i) {
11402                 calc.push(arr[i].c);
11403             }
11404
11405             return {
11406                 g:1,
11407                 c:calc.join(""),
11408                 s:[
11409                     arr[0].s, // year (required)
11410                     "(?:", "-", arr[1].s, // month (optional)
11411                         "(?:", "-", arr[2].s, // day (optional)
11412                             "(?:",
11413                                 "(?:T| )?", // time delimiter -- either a "T" or a single blank space
11414                                 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
11415                                 "(?::", arr[5].s, ")?", // seconds (optional)
11416                                 "(?:(?:\\.|,)(\\d+))?", // decimal fraction of a second (e.g. ",12345" or ".98765") (optional)
11417                                 "(Z|(?:[-+]\\d{2}(?::)?\\d{2}))?", // "Z" (UTC) or "-0530" (UTC offset without colon delimiter) or "+08:00" (UTC offset with colon delimiter) (optional)
11418                             ")?",
11419                         ")?",
11420                     ")?"
11421                 ].join("")
11422             }
11423         },
11424         U: {
11425             g:1,
11426             c:"u = parseInt(results[{0}], 10);\n",
11427             s:"(-?\\d+)" // leading minus sign indicates seconds before UNIX epoch
11428         }
11429     }
11430 });
11431
11432 }());
11433
11434 Ext.apply(Date.prototype, {
11435     // private
11436     dateFormat : function(format) {
11437         if (Date.formatFunctions[format] == null) {
11438             Date.createFormat(format);
11439         }
11440         return Date.formatFunctions[format].call(this);
11441     },
11442
11443     /**
11444      * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
11445      *
11446      * Note: The date string returned by the javascript Date object's toString() method varies
11447      * between browsers (e.g. FF vs IE) and system region settings (e.g. IE in Asia vs IE in America).
11448      * For a given date string e.g. "Thu Oct 25 2007 22:55:35 GMT+0800 (Malay Peninsula Standard Time)",
11449      * getTimezone() first tries to get the timezone abbreviation from between a pair of parentheses
11450      * (which may or may not be present), failing which it proceeds to get the timezone abbreviation
11451      * from the GMT offset portion of the date string.
11452      * @return {String} The abbreviated timezone name (e.g. 'CST', 'PDT', 'EDT', 'MPST' ...).
11453      */
11454     getTimezone : function() {
11455         // the following list shows the differences between date strings from different browsers on a WinXP SP2 machine from an Asian locale:
11456         //
11457         // Opera  : "Thu, 25 Oct 2007 22:53:45 GMT+0800" -- shortest (weirdest) date string of the lot
11458         // 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)
11459         // FF     : "Thu Oct 25 2007 22:55:35 GMT+0800 (Malay Peninsula Standard Time)" -- value in parentheses always gives the correct timezone
11460         // IE     : "Thu Oct 25 22:54:35 UTC+0800 2007" -- (Asian system setting) look for 3-4 letter timezone abbrev
11461         // IE     : "Thu Oct 25 17:06:37 PDT 2007" -- (American system setting) look for 3-4 letter timezone abbrev
11462         //
11463         // this crazy regex attempts to guess the correct timezone abbreviation despite these differences.
11464         // step 1: (?:\((.*)\) -- find timezone in parentheses
11465         // 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
11466         // step 3: remove all non uppercase characters found in step 1 and 2
11467         return this.toString().replace(/^.* (?:\((.*)\)|([A-Z]{1,4})(?:[\-+][0-9]{4})?(?: -?\d+)?)$/, "$1$2").replace(/[^A-Z]/g, "");
11468     },
11469
11470     /**
11471      * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
11472      * @param {Boolean} colon (optional) true to separate the hours and minutes with a colon (defaults to false).
11473      * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600').
11474      */
11475     getGMTOffset : function(colon) {
11476         return (this.getTimezoneOffset() > 0 ? "-" : "+")
11477             + String.leftPad(Math.floor(Math.abs(this.getTimezoneOffset()) / 60), 2, "0")
11478             + (colon ? ":" : "")
11479             + String.leftPad(Math.abs(this.getTimezoneOffset() % 60), 2, "0");
11480     },
11481
11482     /**
11483      * Get the numeric day number of the year, adjusted for leap year.
11484      * @return {Number} 0 to 364 (365 in leap years).
11485      */
11486     getDayOfYear: function() {
11487         var num = 0,
11488             d = this.clone(),
11489             m = this.getMonth(),
11490             i;
11491
11492         for (i = 0, d.setDate(1), d.setMonth(0); i < m; d.setMonth(++i)) {
11493             num += d.getDaysInMonth();
11494         }
11495         return num + this.getDate() - 1;
11496     },
11497
11498     /**
11499      * Get the numeric ISO-8601 week number of the year.
11500      * (equivalent to the format specifier 'W', but without a leading zero).
11501      * @return {Number} 1 to 53
11502      */
11503     getWeekOfYear : function() {
11504         // adapted from http://www.merlyn.demon.co.uk/weekcalc.htm
11505         var ms1d = 864e5, // milliseconds in a day
11506             ms7d = 7 * ms1d; // milliseconds in a week
11507
11508         return function() { // return a closure so constants get calculated only once
11509             var DC3 = Date.UTC(this.getFullYear(), this.getMonth(), this.getDate() + 3) / ms1d, // an Absolute Day Number
11510                 AWN = Math.floor(DC3 / 7), // an Absolute Week Number
11511                 Wyr = new Date(AWN * ms7d).getUTCFullYear();
11512
11513             return AWN - Math.floor(Date.UTC(Wyr, 0, 7) / ms7d) + 1;
11514         }
11515     }(),
11516
11517     /**
11518      * Checks if the current date falls within a leap year.
11519      * @return {Boolean} True if the current date falls within a leap year, false otherwise.
11520      */
11521     isLeapYear : function() {
11522         var year = this.getFullYear();
11523         return !!((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
11524     },
11525
11526     /**
11527      * Get the first day of the current month, adjusted for leap year.  The returned value
11528      * is the numeric day index within the week (0-6) which can be used in conjunction with
11529      * the {@link #monthNames} array to retrieve the textual day name.
11530      * Example:
11531      * <pre><code>
11532 var dt = new Date('1/10/2007');
11533 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
11534 </code></pre>
11535      * @return {Number} The day number (0-6).
11536      */
11537     getFirstDayOfMonth : function() {
11538         var day = (this.getDay() - (this.getDate() - 1)) % 7;
11539         return (day < 0) ? (day + 7) : day;
11540     },
11541
11542     /**
11543      * Get the last day of the current month, adjusted for leap year.  The returned value
11544      * is the numeric day index within the week (0-6) which can be used in conjunction with
11545      * the {@link #monthNames} array to retrieve the textual day name.
11546      * Example:
11547      * <pre><code>
11548 var dt = new Date('1/10/2007');
11549 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
11550 </code></pre>
11551      * @return {Number} The day number (0-6).
11552      */
11553     getLastDayOfMonth : function() {
11554         return this.getLastDateOfMonth().getDay();
11555     },
11556
11557
11558     /**
11559      * Get the date of the first day of the month in which this date resides.
11560      * @return {Date}
11561      */
11562     getFirstDateOfMonth : function() {
11563         return new Date(this.getFullYear(), this.getMonth(), 1);
11564     },
11565
11566     /**
11567      * Get the date of the last day of the month in which this date resides.
11568      * @return {Date}
11569      */
11570     getLastDateOfMonth : function() {
11571         return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
11572     },
11573
11574     /**
11575      * Get the number of days in the current month, adjusted for leap year.
11576      * @return {Number} The number of days in the month.
11577      */
11578     getDaysInMonth: function() {
11579         var daysInMonth = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
11580
11581         return function() { // return a closure for efficiency
11582             var m = this.getMonth();
11583
11584             return m == 1 && this.isLeapYear() ? 29 : daysInMonth[m];
11585         }
11586     }(),
11587
11588     /**
11589      * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
11590      * @return {String} 'st, 'nd', 'rd' or 'th'.
11591      */
11592     getSuffix : function() {
11593         switch (this.getDate()) {
11594             case 1:
11595             case 21:
11596             case 31:
11597                 return "st";
11598             case 2:
11599             case 22:
11600                 return "nd";
11601             case 3:
11602             case 23:
11603                 return "rd";
11604             default:
11605                 return "th";
11606         }
11607     },
11608
11609     /**
11610      * Creates and returns a new Date instance with the exact same date value as the called instance.
11611      * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
11612      * variable will also be changed.  When the intention is to create a new variable that will not
11613      * modify the original instance, you should create a clone.
11614      *
11615      * Example of correctly cloning a date:
11616      * <pre><code>
11617 //wrong way:
11618 var orig = new Date('10/1/2006');
11619 var copy = orig;
11620 copy.setDate(5);
11621 document.write(orig);  //returns 'Thu Oct 05 2006'!
11622
11623 //correct way:
11624 var orig = new Date('10/1/2006');
11625 var copy = orig.clone();
11626 copy.setDate(5);
11627 document.write(orig);  //returns 'Thu Oct 01 2006'
11628 </code></pre>
11629      * @return {Date} The new Date instance.
11630      */
11631     clone : function() {
11632         return new Date(this.getTime());
11633     },
11634
11635     /**
11636      * Checks if the current date is affected by Daylight Saving Time (DST).
11637      * @return {Boolean} True if the current date is affected by DST.
11638      */
11639     isDST : function() {
11640         // adapted from http://extjs.com/forum/showthread.php?p=247172#post247172
11641         // courtesy of @geoffrey.mcgill
11642         return new Date(this.getFullYear(), 0, 1).getTimezoneOffset() != this.getTimezoneOffset();
11643     },
11644
11645     /**
11646      * Attempts to clear all time information from this Date by setting the time to midnight of the same day,
11647      * automatically adjusting for Daylight Saving Time (DST) where applicable.
11648      * (note: DST timezone information for the browser's host operating system is assumed to be up-to-date)
11649      * @param {Boolean} clone true to create a clone of this date, clear the time and return it (defaults to false).
11650      * @return {Date} this or the clone.
11651      */
11652     clearTime : function(clone) {
11653         if (clone) {
11654             return this.clone().clearTime();
11655         }
11656
11657         // get current date before clearing time
11658         var d = this.getDate();
11659
11660         // clear time
11661         this.setHours(0);
11662         this.setMinutes(0);
11663         this.setSeconds(0);
11664         this.setMilliseconds(0);
11665
11666         if (this.getDate() != d) { // account for DST (i.e. day of month changed when setting hour = 0)
11667             // note: DST adjustments are assumed to occur in multiples of 1 hour (this is almost always the case)
11668             // refer to http://www.timeanddate.com/time/aboutdst.html for the (rare) exceptions to this rule
11669
11670             // increment hour until cloned date == current date
11671             for (var hr = 1, c = this.add(Date.HOUR, hr); c.getDate() != d; hr++, c = this.add(Date.HOUR, hr));
11672
11673             this.setDate(d);
11674             this.setHours(c.getHours());
11675         }
11676
11677         return this;
11678     },
11679
11680     /**
11681      * Provides a convenient method for performing basic date arithmetic. This method
11682      * does not modify the Date instance being called - it creates and returns
11683      * a new Date instance containing the resulting date value.
11684      *
11685      * Examples:
11686      * <pre><code>
11687 // Basic usage:
11688 var dt = new Date('10/29/2006').add(Date.DAY, 5);
11689 document.write(dt); //returns 'Fri Nov 03 2006 00:00:00'
11690
11691 // Negative values will be subtracted:
11692 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
11693 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
11694
11695 // You can even chain several calls together in one line:
11696 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
11697 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
11698 </code></pre>
11699      *
11700      * @param {String} interval A valid date interval enum value.
11701      * @param {Number} value The amount to add to the current date.
11702      * @return {Date} The new Date instance.
11703      */
11704     add : function(interval, value) {
11705         var d = this.clone();
11706         if (!interval || value === 0) return d;
11707
11708         switch(interval.toLowerCase()) {
11709             case Date.MILLI:
11710                 d.setMilliseconds(this.getMilliseconds() + value);
11711                 break;
11712             case Date.SECOND:
11713                 d.setSeconds(this.getSeconds() + value);
11714                 break;
11715             case Date.MINUTE:
11716                 d.setMinutes(this.getMinutes() + value);
11717                 break;
11718             case Date.HOUR:
11719                 d.setHours(this.getHours() + value);
11720                 break;
11721             case Date.DAY:
11722                 d.setDate(this.getDate() + value);
11723                 break;
11724             case Date.MONTH:
11725                 var day = this.getDate();
11726                 if (day > 28) {
11727                     day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
11728                 }
11729                 d.setDate(day);
11730                 d.setMonth(this.getMonth() + value);
11731                 break;
11732             case Date.YEAR:
11733                 d.setFullYear(this.getFullYear() + value);
11734                 break;
11735         }
11736         return d;
11737     },
11738
11739     /**
11740      * Checks if this date falls on or between the given start and end dates.
11741      * @param {Date} start Start date
11742      * @param {Date} end End date
11743      * @return {Boolean} true if this date falls on or between the given start and end dates.
11744      */
11745     between : function(start, end) {
11746         var t = this.getTime();
11747         return start.getTime() <= t && t <= end.getTime();
11748     }
11749 });
11750
11751
11752 /**
11753  * Formats a date given the supplied format string.
11754  * @param {String} format The format string.
11755  * @return {String} The formatted date.
11756  * @method format
11757  */
11758 Date.prototype.format = Date.prototype.dateFormat;
11759
11760
11761 // private
11762 if (Ext.isSafari && (navigator.userAgent.match(/WebKit\/(\d+)/)[1] || NaN) < 420) {
11763     Ext.apply(Date.prototype, {
11764         _xMonth : Date.prototype.setMonth,
11765         _xDate  : Date.prototype.setDate,
11766
11767         // Bug in Safari 1.3, 2.0 (WebKit build < 420)
11768         // Date.setMonth does not work consistently if iMonth is not 0-11
11769         setMonth : function(num) {
11770             if (num <= -1) {
11771                 var n = Math.ceil(-num),
11772                     back_year = Math.ceil(n / 12),
11773                     month = (n % 12) ? 12 - n % 12 : 0;
11774
11775                 this.setFullYear(this.getFullYear() - back_year);
11776
11777                 return this._xMonth(month);
11778             } else {
11779                 return this._xMonth(num);
11780             }
11781         },
11782
11783         // Bug in setDate() method (resolved in WebKit build 419.3, so to be safe we target Webkit builds < 420)
11784         // The parameter for Date.setDate() is converted to a signed byte integer in Safari
11785         // http://brianary.blogspot.com/2006/03/safari-date-bug.html
11786         setDate : function(d) {
11787             // use setTime() to workaround setDate() bug
11788             // subtract current day of month in milliseconds, then add desired day of month in milliseconds
11789             return this.setTime(this.getTime() - (this.getDate() - d) * 864e5);
11790         }
11791     });
11792 }
11793
11794
11795
11796 /* Some basic Date tests... (requires Firebug)
11797
11798 Date.parseDate('', 'c'); // call Date.parseDate() once to force computation of regex string so we can console.log() it
11799 console.log('Insane Regex for "c" format: %o', Date.parseCodes.c.s); // view the insane regex for the "c" format specifier
11800
11801 // standard tests
11802 console.group('Standard Date.parseDate() Tests');
11803     console.log('Date.parseDate("2009-01-05T11:38:56", "c")               = %o', Date.parseDate("2009-01-05T11:38:56", "c")); // assumes browser's timezone setting
11804     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
11805     console.log('Date.parseDate("2009-03-03T13:36:54,101000Z", "c")       = %o', Date.parseDate("2009-03-03T13:36:54,101000Z", "c")); // UTC
11806     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
11807     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
11808 console.groupEnd();
11809
11810 // ISO-8601 format as specified in http://www.w3.org/TR/NOTE-datetime
11811 // -- accepts ALL 6 levels of date-time granularity
11812 console.group('ISO-8601 Granularity Test (see http://www.w3.org/TR/NOTE-datetime)');
11813     console.log('Date.parseDate("1997", "c")                              = %o', Date.parseDate("1997", "c")); // YYYY (e.g. 1997)
11814     console.log('Date.parseDate("1997-07", "c")                           = %o', Date.parseDate("1997-07", "c")); // YYYY-MM (e.g. 1997-07)
11815     console.log('Date.parseDate("1997-07-16", "c")                        = %o', Date.parseDate("1997-07-16", "c")); // YYYY-MM-DD (e.g. 1997-07-16)
11816     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)
11817     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)
11818     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)
11819     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)
11820     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
11821 console.groupEnd();
11822
11823 //*/
11824 /**
11825  * @class Ext.util.MixedCollection
11826  * @extends Ext.util.Observable
11827  * A Collection class that maintains both numeric indexes and keys and exposes events.
11828  * @constructor
11829  * @param {Boolean} allowFunctions Specify <tt>true</tt> if the {@link #addAll}
11830  * function should add function references to the collection. Defaults to
11831  * <tt>false</tt>.
11832  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
11833  * and return the key value for that item.  This is used when available to look up the key on items that
11834  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
11835  * equivalent to providing an implementation for the {@link #getKey} method.
11836  */
11837 Ext.util.MixedCollection = function(allowFunctions, keyFn){
11838     this.items = [];
11839     this.map = {};
11840     this.keys = [];
11841     this.length = 0;
11842     this.addEvents(
11843         /**
11844          * @event clear
11845          * Fires when the collection is cleared.
11846          */
11847         'clear',
11848         /**
11849          * @event add
11850          * Fires when an item is added to the collection.
11851          * @param {Number} index The index at which the item was added.
11852          * @param {Object} o The item added.
11853          * @param {String} key The key associated with the added item.
11854          */
11855         'add',
11856         /**
11857          * @event replace
11858          * Fires when an item is replaced in the collection.
11859          * @param {String} key he key associated with the new added.
11860          * @param {Object} old The item being replaced.
11861          * @param {Object} new The new item.
11862          */
11863         'replace',
11864         /**
11865          * @event remove
11866          * Fires when an item is removed from the collection.
11867          * @param {Object} o The item being removed.
11868          * @param {String} key (optional) The key associated with the removed item.
11869          */
11870         'remove',
11871         'sort'
11872     );
11873     this.allowFunctions = allowFunctions === true;
11874     if(keyFn){
11875         this.getKey = keyFn;
11876     }
11877     Ext.util.MixedCollection.superclass.constructor.call(this);
11878 };
11879
11880 Ext.extend(Ext.util.MixedCollection, Ext.util.Observable, {
11881
11882     /**
11883      * @cfg {Boolean} allowFunctions Specify <tt>true</tt> if the {@link #addAll}
11884      * function should add function references to the collection. Defaults to
11885      * <tt>false</tt>.
11886      */
11887     allowFunctions : false,
11888
11889     /**
11890      * Adds an item to the collection. Fires the {@link #add} event when complete.
11891      * @param {String} key <p>The key to associate with the item, or the new item.</p>
11892      * <p>If a {@link #getKey} implementation was specified for this MixedCollection,
11893      * or if the key of the stored items is in a property called <tt><b>id</b></tt>,
11894      * the MixedCollection will be able to <i>derive</i> the key for the new item.
11895      * In this case just pass the new item in this parameter.</p>
11896      * @param {Object} o The item to add.
11897      * @return {Object} The item added.
11898      */
11899     add : function(key, o){
11900         if(arguments.length == 1){
11901             o = arguments[0];
11902             key = this.getKey(o);
11903         }
11904         if(typeof key != 'undefined' && key !== null){
11905             var old = this.map[key];
11906             if(typeof old != 'undefined'){
11907                 return this.replace(key, o);
11908             }
11909             this.map[key] = o;
11910         }
11911         this.length++;
11912         this.items.push(o);
11913         this.keys.push(key);
11914         this.fireEvent('add', this.length-1, o, key);
11915         return o;
11916     },
11917
11918     /**
11919       * MixedCollection has a generic way to fetch keys if you implement getKey.  The default implementation
11920       * simply returns <b><code>item.id</code></b> but you can provide your own implementation
11921       * to return a different value as in the following examples:<pre><code>
11922 // normal way
11923 var mc = new Ext.util.MixedCollection();
11924 mc.add(someEl.dom.id, someEl);
11925 mc.add(otherEl.dom.id, otherEl);
11926 //and so on
11927
11928 // using getKey
11929 var mc = new Ext.util.MixedCollection();
11930 mc.getKey = function(el){
11931    return el.dom.id;
11932 };
11933 mc.add(someEl);
11934 mc.add(otherEl);
11935
11936 // or via the constructor
11937 var mc = new Ext.util.MixedCollection(false, function(el){
11938    return el.dom.id;
11939 });
11940 mc.add(someEl);
11941 mc.add(otherEl);
11942      * </code></pre>
11943      * @param {Object} item The item for which to find the key.
11944      * @return {Object} The key for the passed item.
11945      */
11946     getKey : function(o){
11947          return o.id;
11948     },
11949
11950     /**
11951      * Replaces an item in the collection. Fires the {@link #replace} event when complete.
11952      * @param {String} key <p>The key associated with the item to replace, or the replacement item.</p>
11953      * <p>If you supplied a {@link #getKey} implementation for this MixedCollection, or if the key
11954      * of your stored items is in a property called <tt><b>id</b></tt>, then the MixedCollection
11955      * will be able to <i>derive</i> the key of the replacement item. If you want to replace an item
11956      * with one having the same key value, then just pass the replacement item in this parameter.</p>
11957      * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate
11958      * with that key.
11959      * @return {Object}  The new item.
11960      */
11961     replace : function(key, o){
11962         if(arguments.length == 1){
11963             o = arguments[0];
11964             key = this.getKey(o);
11965         }
11966         var old = this.map[key];
11967         if(typeof key == 'undefined' || key === null || typeof old == 'undefined'){
11968              return this.add(key, o);
11969         }
11970         var index = this.indexOfKey(key);
11971         this.items[index] = o;
11972         this.map[key] = o;
11973         this.fireEvent('replace', key, old, o);
11974         return o;
11975     },
11976
11977     /**
11978      * Adds all elements of an Array or an Object to the collection.
11979      * @param {Object/Array} objs An Object containing properties which will be added
11980      * to the collection, or an Array of values, each of which are added to the collection.
11981      * Functions references will be added to the collection if <code>{@link #allowFunctions}</code>
11982      * has been set to <tt>true</tt>.
11983      */
11984     addAll : function(objs){
11985         if(arguments.length > 1 || Ext.isArray(objs)){
11986             var args = arguments.length > 1 ? arguments : objs;
11987             for(var i = 0, len = args.length; i < len; i++){
11988                 this.add(args[i]);
11989             }
11990         }else{
11991             for(var key in objs){
11992                 if(this.allowFunctions || typeof objs[key] != 'function'){
11993                     this.add(key, objs[key]);
11994                 }
11995             }
11996         }
11997     },
11998
11999     /**
12000      * Executes the specified function once for every item in the collection, passing the following arguments:
12001      * <div class="mdetail-params"><ul>
12002      * <li><b>item</b> : Mixed<p class="sub-desc">The collection item</p></li>
12003      * <li><b>index</b> : Number<p class="sub-desc">The item's index</p></li>
12004      * <li><b>length</b> : Number<p class="sub-desc">The total number of items in the collection</p></li>
12005      * </ul></div>
12006      * The function should return a boolean value. Returning false from the function will stop the iteration.
12007      * @param {Function} fn The function to execute for each item.
12008      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the current item in the iteration.
12009      */
12010     each : function(fn, scope){
12011         var items = [].concat(this.items); // each safe for removal
12012         for(var i = 0, len = items.length; i < len; i++){
12013             if(fn.call(scope || items[i], items[i], i, len) === false){
12014                 break;
12015             }
12016         }
12017     },
12018
12019     /**
12020      * Executes the specified function once for every key in the collection, passing each
12021      * key, and its associated item as the first two parameters.
12022      * @param {Function} fn The function to execute for each item.
12023      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the browser window.
12024      */
12025     eachKey : function(fn, scope){
12026         for(var i = 0, len = this.keys.length; i < len; i++){
12027             fn.call(scope || window, this.keys[i], this.items[i], i, len);
12028         }
12029     },
12030
12031     /**
12032      * Returns the first item in the collection which elicits a true return value from the
12033      * passed selection function.
12034      * @param {Function} fn The selection function to execute for each item.
12035      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the browser window.
12036      * @return {Object} The first item in the collection which returned true from the selection function.
12037      */
12038     find : function(fn, scope){
12039         for(var i = 0, len = this.items.length; i < len; i++){
12040             if(fn.call(scope || window, this.items[i], this.keys[i])){
12041                 return this.items[i];
12042             }
12043         }
12044         return null;
12045     },
12046
12047     /**
12048      * Inserts an item at the specified index in the collection. Fires the {@link #add} event when complete.
12049      * @param {Number} index The index to insert the item at.
12050      * @param {String} key The key to associate with the new item, or the item itself.
12051      * @param {Object} o (optional) If the second parameter was a key, the new item.
12052      * @return {Object} The item inserted.
12053      */
12054     insert : function(index, key, o){
12055         if(arguments.length == 2){
12056             o = arguments[1];
12057             key = this.getKey(o);
12058         }
12059         if(this.containsKey(key)){
12060             this.suspendEvents();
12061             this.removeKey(key);
12062             this.resumeEvents();
12063         }
12064         if(index >= this.length){
12065             return this.add(key, o);
12066         }
12067         this.length++;
12068         this.items.splice(index, 0, o);
12069         if(typeof key != 'undefined' && key !== null){
12070             this.map[key] = o;
12071         }
12072         this.keys.splice(index, 0, key);
12073         this.fireEvent('add', index, o, key);
12074         return o;
12075     },
12076
12077     /**
12078      * Remove an item from the collection.
12079      * @param {Object} o The item to remove.
12080      * @return {Object} The item removed or false if no item was removed.
12081      */
12082     remove : function(o){
12083         return this.removeAt(this.indexOf(o));
12084     },
12085
12086     /**
12087      * Remove an item from a specified index in the collection. Fires the {@link #remove} event when complete.
12088      * @param {Number} index The index within the collection of the item to remove.
12089      * @return {Object} The item removed or false if no item was removed.
12090      */
12091     removeAt : function(index){
12092         if(index < this.length && index >= 0){
12093             this.length--;
12094             var o = this.items[index];
12095             this.items.splice(index, 1);
12096             var key = this.keys[index];
12097             if(typeof key != 'undefined'){
12098                 delete this.map[key];
12099             }
12100             this.keys.splice(index, 1);
12101             this.fireEvent('remove', o, key);
12102             return o;
12103         }
12104         return false;
12105     },
12106
12107     /**
12108      * Removed an item associated with the passed key fom the collection.
12109      * @param {String} key The key of the item to remove.
12110      * @return {Object} The item removed or false if no item was removed.
12111      */
12112     removeKey : function(key){
12113         return this.removeAt(this.indexOfKey(key));
12114     },
12115
12116     /**
12117      * Returns the number of items in the collection.
12118      * @return {Number} the number of items in the collection.
12119      */
12120     getCount : function(){
12121         return this.length;
12122     },
12123
12124     /**
12125      * Returns index within the collection of the passed Object.
12126      * @param {Object} o The item to find the index of.
12127      * @return {Number} index of the item. Returns -1 if not found.
12128      */
12129     indexOf : function(o){
12130         return this.items.indexOf(o);
12131     },
12132
12133     /**
12134      * Returns index within the collection of the passed key.
12135      * @param {String} key The key to find the index of.
12136      * @return {Number} index of the key.
12137      */
12138     indexOfKey : function(key){
12139         return this.keys.indexOf(key);
12140     },
12141
12142     /**
12143      * Returns the item associated with the passed key OR index.
12144      * Key has priority over index.  This is the equivalent
12145      * of calling {@link #key} first, then if nothing matched calling {@link #itemAt}.
12146      * @param {String/Number} key The key or index of the item.
12147      * @return {Object} If the item is found, returns the item.  If the item was not found, returns <tt>undefined</tt>.
12148      * If an item was found, but is a Class, returns <tt>null</tt>.
12149      */
12150     item : function(key){
12151         var mk = this.map[key],
12152             item = mk !== undefined ? mk : (typeof key == 'number') ? this.items[key] : undefined;
12153         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
12154     },
12155
12156     /**
12157      * Returns the item at the specified index.
12158      * @param {Number} index The index of the item.
12159      * @return {Object} The item at the specified index.
12160      */
12161     itemAt : function(index){
12162         return this.items[index];
12163     },
12164
12165     /**
12166      * Returns the item associated with the passed key.
12167      * @param {String/Number} key The key of the item.
12168      * @return {Object} The item associated with the passed key.
12169      */
12170     key : function(key){
12171         return this.map[key];
12172     },
12173
12174     /**
12175      * Returns true if the collection contains the passed Object as an item.
12176      * @param {Object} o  The Object to look for in the collection.
12177      * @return {Boolean} True if the collection contains the Object as an item.
12178      */
12179     contains : function(o){
12180         return this.indexOf(o) != -1;
12181     },
12182
12183     /**
12184      * Returns true if the collection contains the passed Object as a key.
12185      * @param {String} key The key to look for in the collection.
12186      * @return {Boolean} True if the collection contains the Object as a key.
12187      */
12188     containsKey : function(key){
12189         return typeof this.map[key] != 'undefined';
12190     },
12191
12192     /**
12193      * Removes all items from the collection.  Fires the {@link #clear} event when complete.
12194      */
12195     clear : function(){
12196         this.length = 0;
12197         this.items = [];
12198         this.keys = [];
12199         this.map = {};
12200         this.fireEvent('clear');
12201     },
12202
12203     /**
12204      * Returns the first item in the collection.
12205      * @return {Object} the first item in the collection..
12206      */
12207     first : function(){
12208         return this.items[0];
12209     },
12210
12211     /**
12212      * Returns the last item in the collection.
12213      * @return {Object} the last item in the collection..
12214      */
12215     last : function(){
12216         return this.items[this.length-1];
12217     },
12218
12219     /**
12220      * @private
12221      * Performs the actual sorting based on a direction and a sorting function. Internally,
12222      * this creates a temporary array of all items in the MixedCollection, sorts it and then writes
12223      * the sorted array data back into this.items and this.keys
12224      * @param {String} property Property to sort by ('key', 'value', or 'index')
12225      * @param {String} dir (optional) Direction to sort 'ASC' or 'DESC'. Defaults to 'ASC'.
12226      * @param {Function} fn (optional) Comparison function that defines the sort order.
12227      * Defaults to sorting by numeric value.
12228      */
12229     _sort : function(property, dir, fn){
12230         var i, len,
12231             dsc   = String(dir).toUpperCase() == 'DESC' ? -1 : 1,
12232
12233             //this is a temporary array used to apply the sorting function
12234             c     = [],
12235             keys  = this.keys,
12236             items = this.items;
12237
12238         //default to a simple sorter function if one is not provided
12239         fn = fn || function(a, b) {
12240             return a - b;
12241         };
12242
12243         //copy all the items into a temporary array, which we will sort
12244         for(i = 0, len = items.length; i < len; i++){
12245             c[c.length] = {
12246                 key  : keys[i],
12247                 value: items[i],
12248                 index: i
12249             };
12250         }
12251
12252         //sort the temporary array
12253         c.sort(function(a, b){
12254             var v = fn(a[property], b[property]) * dsc;
12255             if(v === 0){
12256                 v = (a.index < b.index ? -1 : 1);
12257             }
12258             return v;
12259         });
12260
12261         //copy the temporary array back into the main this.items and this.keys objects
12262         for(i = 0, len = c.length; i < len; i++){
12263             items[i] = c[i].value;
12264             keys[i]  = c[i].key;
12265         }
12266
12267         this.fireEvent('sort', this);
12268     },
12269
12270     /**
12271      * Sorts this collection by <b>item</b> value with the passed comparison function.
12272      * @param {String} direction (optional) 'ASC' or 'DESC'. Defaults to 'ASC'.
12273      * @param {Function} fn (optional) Comparison function that defines the sort order.
12274      * Defaults to sorting by numeric value.
12275      */
12276     sort : function(dir, fn){
12277         this._sort('value', dir, fn);
12278     },
12279
12280     /**
12281      * Reorders each of the items based on a mapping from old index to new index. Internally this
12282      * just translates into a sort. The 'sort' event is fired whenever reordering has occured.
12283      * @param {Object} mapping Mapping from old item index to new item index
12284      */
12285     reorder: function(mapping) {
12286         this.suspendEvents();
12287
12288         var items = this.items,
12289             index = 0,
12290             length = items.length,
12291             order = [],
12292             remaining = [],
12293             oldIndex;
12294
12295         //object of {oldPosition: newPosition} reversed to {newPosition: oldPosition}
12296         for (oldIndex in mapping) {
12297             order[mapping[oldIndex]] = items[oldIndex];
12298         }
12299
12300         for (index = 0; index < length; index++) {
12301             if (mapping[index] == undefined) {
12302                 remaining.push(items[index]);
12303             }
12304         }
12305
12306         for (index = 0; index < length; index++) {
12307             if (order[index] == undefined) {
12308                 order[index] = remaining.shift();
12309             }
12310         }
12311
12312         this.clear();
12313         this.addAll(order);
12314
12315         this.resumeEvents();
12316         this.fireEvent('sort', this);
12317     },
12318
12319     /**
12320      * Sorts this collection by <b>key</b>s.
12321      * @param {String} direction (optional) 'ASC' or 'DESC'. Defaults to 'ASC'.
12322      * @param {Function} fn (optional) Comparison function that defines the sort order.
12323      * Defaults to sorting by case insensitive string.
12324      */
12325     keySort : function(dir, fn){
12326         this._sort('key', dir, fn || function(a, b){
12327             var v1 = String(a).toUpperCase(), v2 = String(b).toUpperCase();
12328             return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
12329         });
12330     },
12331
12332     /**
12333      * Returns a range of items in this collection
12334      * @param {Number} startIndex (optional) The starting index. Defaults to 0.
12335      * @param {Number} endIndex (optional) The ending index. Defaults to the last item.
12336      * @return {Array} An array of items
12337      */
12338     getRange : function(start, end){
12339         var items = this.items;
12340         if(items.length < 1){
12341             return [];
12342         }
12343         start = start || 0;
12344         end = Math.min(typeof end == 'undefined' ? this.length-1 : end, this.length-1);
12345         var i, r = [];
12346         if(start <= end){
12347             for(i = start; i <= end; i++) {
12348                 r[r.length] = items[i];
12349             }
12350         }else{
12351             for(i = start; i >= end; i--) {
12352                 r[r.length] = items[i];
12353             }
12354         }
12355         return r;
12356     },
12357
12358     /**
12359      * Filter the <i>objects</i> in this collection by a specific property.
12360      * Returns a new collection that has been filtered.
12361      * @param {String} property A property on your objects
12362      * @param {String/RegExp} value Either string that the property values
12363      * should start with or a RegExp to test against the property
12364      * @param {Boolean} anyMatch (optional) True to match any part of the string, not just the beginning
12365      * @param {Boolean} caseSensitive (optional) True for case sensitive comparison (defaults to False).
12366      * @return {MixedCollection} The new filtered collection
12367      */
12368     filter : function(property, value, anyMatch, caseSensitive){
12369         if(Ext.isEmpty(value, false)){
12370             return this.clone();
12371         }
12372         value = this.createValueMatcher(value, anyMatch, caseSensitive);
12373         return this.filterBy(function(o){
12374             return o && value.test(o[property]);
12375         });
12376     },
12377
12378     /**
12379      * Filter by a function. Returns a <i>new</i> collection that has been filtered.
12380      * The passed function will be called with each object in the collection.
12381      * If the function returns true, the value is included otherwise it is filtered.
12382      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
12383      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to this MixedCollection.
12384      * @return {MixedCollection} The new filtered collection
12385      */
12386     filterBy : function(fn, scope){
12387         var r = new Ext.util.MixedCollection();
12388         r.getKey = this.getKey;
12389         var k = this.keys, it = this.items;
12390         for(var i = 0, len = it.length; i < len; i++){
12391             if(fn.call(scope||this, it[i], k[i])){
12392                 r.add(k[i], it[i]);
12393             }
12394         }
12395         return r;
12396     },
12397
12398     /**
12399      * Finds the index of the first matching object in this collection by a specific property/value.
12400      * @param {String} property The name of a property on your objects.
12401      * @param {String/RegExp} value A string that the property values
12402      * should start with or a RegExp to test against the property.
12403      * @param {Number} start (optional) The index to start searching at (defaults to 0).
12404      * @param {Boolean} anyMatch (optional) True to match any part of the string, not just the beginning.
12405      * @param {Boolean} caseSensitive (optional) True for case sensitive comparison.
12406      * @return {Number} The matched index or -1
12407      */
12408     findIndex : function(property, value, start, anyMatch, caseSensitive){
12409         if(Ext.isEmpty(value, false)){
12410             return -1;
12411         }
12412         value = this.createValueMatcher(value, anyMatch, caseSensitive);
12413         return this.findIndexBy(function(o){
12414             return o && value.test(o[property]);
12415         }, null, start);
12416     },
12417
12418     /**
12419      * Find the index of the first matching object in this collection by a function.
12420      * If the function returns <i>true</i> it is considered a match.
12421      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key).
12422      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to this MixedCollection.
12423      * @param {Number} start (optional) The index to start searching at (defaults to 0).
12424      * @return {Number} The matched index or -1
12425      */
12426     findIndexBy : function(fn, scope, start){
12427         var k = this.keys, it = this.items;
12428         for(var i = (start||0), len = it.length; i < len; i++){
12429             if(fn.call(scope||this, it[i], k[i])){
12430                 return i;
12431             }
12432         }
12433         return -1;
12434     },
12435
12436     /**
12437      * Returns a regular expression based on the given value and matching options. This is used internally for finding and filtering,
12438      * and by Ext.data.Store#filter
12439      * @private
12440      * @param {String} value The value to create the regex for. This is escaped using Ext.escapeRe
12441      * @param {Boolean} anyMatch True to allow any match - no regex start/end line anchors will be added. Defaults to false
12442      * @param {Boolean} caseSensitive True to make the regex case sensitive (adds 'i' switch to regex). Defaults to false.
12443      * @param {Boolean} exactMatch True to force exact match (^ and $ characters added to the regex). Defaults to false. Ignored if anyMatch is true.
12444      */
12445     createValueMatcher : function(value, anyMatch, caseSensitive, exactMatch) {
12446         if (!value.exec) { // not a regex
12447             var er = Ext.escapeRe;
12448             value = String(value);
12449
12450             if (anyMatch === true) {
12451                 value = er(value);
12452             } else {
12453                 value = '^' + er(value);
12454                 if (exactMatch === true) {
12455                     value += '$';
12456                 }
12457             }
12458             value = new RegExp(value, caseSensitive ? '' : 'i');
12459          }
12460          return value;
12461     },
12462
12463     /**
12464      * Creates a shallow copy of this collection
12465      * @return {MixedCollection}
12466      */
12467     clone : function(){
12468         var r = new Ext.util.MixedCollection();
12469         var k = this.keys, it = this.items;
12470         for(var i = 0, len = it.length; i < len; i++){
12471             r.add(k[i], it[i]);
12472         }
12473         r.getKey = this.getKey;
12474         return r;
12475     }
12476 });
12477 /**
12478  * This method calls {@link #item item()}.
12479  * Returns the item associated with the passed key OR index. Key has priority
12480  * over index.  This is the equivalent of calling {@link #key} first, then if
12481  * nothing matched calling {@link #itemAt}.
12482  * @param {String/Number} key The key or index of the item.
12483  * @return {Object} If the item is found, returns the item.  If the item was
12484  * not found, returns <tt>undefined</tt>. If an item was found, but is a Class,
12485  * returns <tt>null</tt>.
12486  */
12487 Ext.util.MixedCollection.prototype.get = Ext.util.MixedCollection.prototype.item;
12488 /**
12489  * @class Ext.util.JSON
12490  * Modified version of Douglas Crockford"s json.js that doesn"t
12491  * mess with the Object prototype
12492  * http://www.json.org/js.html
12493  * @singleton
12494  */
12495 Ext.util.JSON = new (function(){
12496     var useHasOwn = !!{}.hasOwnProperty,
12497         isNative = function() {
12498             var useNative = null;
12499
12500             return function() {
12501                 if (useNative === null) {
12502                     useNative = Ext.USE_NATIVE_JSON && window.JSON && JSON.toString() == '[object JSON]';
12503                 }
12504         
12505                 return useNative;
12506             };
12507         }(),
12508         pad = function(n) {
12509             return n < 10 ? "0" + n : n;
12510         },
12511         doDecode = function(json){
12512             return eval("(" + json + ')');    
12513         },
12514         doEncode = function(o){
12515             if(!Ext.isDefined(o) || o === null){
12516                 return "null";
12517             }else if(Ext.isArray(o)){
12518                 return encodeArray(o);
12519             }else if(Ext.isDate(o)){
12520                 return Ext.util.JSON.encodeDate(o);
12521             }else if(Ext.isString(o)){
12522                 return encodeString(o);
12523             }else if(typeof o == "number"){
12524                 //don't use isNumber here, since finite checks happen inside isNumber
12525                 return isFinite(o) ? String(o) : "null";
12526             }else if(Ext.isBoolean(o)){
12527                 return String(o);
12528             }else {
12529                 var a = ["{"], b, i, v;
12530                 for (i in o) {
12531                     // don't encode DOM objects
12532                     if(!o.getElementsByTagName){
12533                         if(!useHasOwn || o.hasOwnProperty(i)) {
12534                             v = o[i];
12535                             switch (typeof v) {
12536                             case "undefined":
12537                             case "function":
12538                             case "unknown":
12539                                 break;
12540                             default:
12541                                 if(b){
12542                                     a.push(',');
12543                                 }
12544                                 a.push(doEncode(i), ":",
12545                                         v === null ? "null" : doEncode(v));
12546                                 b = true;
12547                             }
12548                         }
12549                     }
12550                 }
12551                 a.push("}");
12552                 return a.join("");
12553             }    
12554         },
12555         m = {
12556             "\b": '\\b',
12557             "\t": '\\t',
12558             "\n": '\\n',
12559             "\f": '\\f',
12560             "\r": '\\r',
12561             '"' : '\\"',
12562             "\\": '\\\\'
12563         },
12564         encodeString = function(s){
12565             if (/["\\\x00-\x1f]/.test(s)) {
12566                 return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
12567                     var c = m[b];
12568                     if(c){
12569                         return c;
12570                     }
12571                     c = b.charCodeAt();
12572                     return "\\u00" +
12573                         Math.floor(c / 16).toString(16) +
12574                         (c % 16).toString(16);
12575                 }) + '"';
12576             }
12577             return '"' + s + '"';
12578         },
12579         encodeArray = function(o){
12580             var a = ["["], b, i, l = o.length, v;
12581                 for (i = 0; i < l; i += 1) {
12582                     v = o[i];
12583                     switch (typeof v) {
12584                         case "undefined":
12585                         case "function":
12586                         case "unknown":
12587                             break;
12588                         default:
12589                             if (b) {
12590                                 a.push(',');
12591                             }
12592                             a.push(v === null ? "null" : Ext.util.JSON.encode(v));
12593                             b = true;
12594                     }
12595                 }
12596                 a.push("]");
12597                 return a.join("");
12598         };
12599
12600     /**
12601      * <p>Encodes a Date. This returns the actual string which is inserted into the JSON string as the literal expression.
12602      * <b>The returned value includes enclosing double quotation marks.</b></p>
12603      * <p>The default return format is "yyyy-mm-ddThh:mm:ss".</p>
12604      * <p>To override this:</p><pre><code>
12605 Ext.util.JSON.encodeDate = function(d) {
12606     return d.format('"Y-m-d"');
12607 };
12608 </code></pre>
12609      * @param {Date} d The Date to encode
12610      * @return {String} The string literal to use in a JSON string.
12611      */
12612     this.encodeDate = function(o){
12613         return '"' + o.getFullYear() + "-" +
12614                 pad(o.getMonth() + 1) + "-" +
12615                 pad(o.getDate()) + "T" +
12616                 pad(o.getHours()) + ":" +
12617                 pad(o.getMinutes()) + ":" +
12618                 pad(o.getSeconds()) + '"';
12619     };
12620
12621     /**
12622      * Encodes an Object, Array or other value
12623      * @param {Mixed} o The variable to encode
12624      * @return {String} The JSON string
12625      */
12626     this.encode = function() {
12627         var ec;
12628         return function(o) {
12629             if (!ec) {
12630                 // setup encoding function on first access
12631                 ec = isNative() ? JSON.stringify : doEncode;
12632             }
12633             return ec(o);
12634         };
12635     }();
12636
12637
12638     /**
12639      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError unless the safe option is set.
12640      * @param {String} json The JSON string
12641      * @return {Object} The resulting object
12642      */
12643     this.decode = function() {
12644         var dc;
12645         return function(json) {
12646             if (!dc) {
12647                 // setup decoding function on first access
12648                 dc = isNative() ? JSON.parse : doDecode;
12649             }
12650             return dc(json);
12651         };
12652     }();
12653
12654 })();
12655 /**
12656  * Shorthand for {@link Ext.util.JSON#encode}
12657  * @param {Mixed} o The variable to encode
12658  * @return {String} The JSON string
12659  * @member Ext
12660  * @method encode
12661  */
12662 Ext.encode = Ext.util.JSON.encode;
12663 /**
12664  * Shorthand for {@link Ext.util.JSON#decode}
12665  * @param {String} json The JSON string
12666  * @param {Boolean} safe (optional) Whether to return null or throw an exception if the JSON is invalid.
12667  * @return {Object} The resulting object
12668  * @member Ext
12669  * @method decode
12670  */
12671 Ext.decode = Ext.util.JSON.decode;
12672 /**
12673  * @class Ext.util.Format
12674  * Reusable data formatting functions
12675  * @singleton
12676  */
12677 Ext.util.Format = function(){
12678     var trimRe = /^\s+|\s+$/g,
12679         stripTagsRE = /<\/?[^>]+>/gi,
12680         stripScriptsRe = /(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig,
12681         nl2brRe = /\r?\n/g;
12682
12683     return {
12684         /**
12685          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
12686          * @param {String} value The string to truncate
12687          * @param {Number} length The maximum length to allow before truncating
12688          * @param {Boolean} word True to try to find a common work break
12689          * @return {String} The converted text
12690          */
12691         ellipsis : function(value, len, word){
12692             if(value && value.length > len){
12693                 if(word){
12694                     var vs = value.substr(0, len - 2),
12695                         index = Math.max(vs.lastIndexOf(' '), vs.lastIndexOf('.'), vs.lastIndexOf('!'), vs.lastIndexOf('?'));
12696                     if(index == -1 || index < (len - 15)){
12697                         return value.substr(0, len - 3) + "...";
12698                     }else{
12699                         return vs.substr(0, index) + "...";
12700                     }
12701                 } else{
12702                     return value.substr(0, len - 3) + "...";
12703                 }
12704             }
12705             return value;
12706         },
12707
12708         /**
12709          * Checks a reference and converts it to empty string if it is undefined
12710          * @param {Mixed} value Reference to check
12711          * @return {Mixed} Empty string if converted, otherwise the original value
12712          */
12713         undef : function(value){
12714             return value !== undefined ? value : "";
12715         },
12716
12717         /**
12718          * Checks a reference and converts it to the default value if it's empty
12719          * @param {Mixed} value Reference to check
12720          * @param {String} defaultValue The value to insert of it's undefined (defaults to "")
12721          * @return {String}
12722          */
12723         defaultValue : function(value, defaultValue){
12724             return value !== undefined && value !== '' ? value : defaultValue;
12725         },
12726
12727         /**
12728          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
12729          * @param {String} value The string to encode
12730          * @return {String} The encoded text
12731          */
12732         htmlEncode : function(value){
12733             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
12734         },
12735
12736         /**
12737          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
12738          * @param {String} value The string to decode
12739          * @return {String} The decoded text
12740          */
12741         htmlDecode : function(value){
12742             return !value ? value : String(value).replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"').replace(/&amp;/g, "&");
12743         },
12744
12745         /**
12746          * Trims any whitespace from either side of a string
12747          * @param {String} value The text to trim
12748          * @return {String} The trimmed text
12749          */
12750         trim : function(value){
12751             return String(value).replace(trimRe, "");
12752         },
12753
12754         /**
12755          * Returns a substring from within an original string
12756          * @param {String} value The original text
12757          * @param {Number} start The start index of the substring
12758          * @param {Number} length The length of the substring
12759          * @return {String} The substring
12760          */
12761         substr : function(value, start, length){
12762             return String(value).substr(start, length);
12763         },
12764
12765         /**
12766          * Converts a string to all lower case letters
12767          * @param {String} value The text to convert
12768          * @return {String} The converted text
12769          */
12770         lowercase : function(value){
12771             return String(value).toLowerCase();
12772         },
12773
12774         /**
12775          * Converts a string to all upper case letters
12776          * @param {String} value The text to convert
12777          * @return {String} The converted text
12778          */
12779         uppercase : function(value){
12780             return String(value).toUpperCase();
12781         },
12782
12783         /**
12784          * Converts the first character only of a string to upper case
12785          * @param {String} value The text to convert
12786          * @return {String} The converted text
12787          */
12788         capitalize : function(value){
12789             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
12790         },
12791
12792         // private
12793         call : function(value, fn){
12794             if(arguments.length > 2){
12795                 var args = Array.prototype.slice.call(arguments, 2);
12796                 args.unshift(value);
12797                 return eval(fn).apply(window, args);
12798             }else{
12799                 return eval(fn).call(window, value);
12800             }
12801         },
12802
12803         /**
12804          * Format a number as US currency
12805          * @param {Number/String} value The numeric value to format
12806          * @return {String} The formatted currency string
12807          */
12808         usMoney : function(v){
12809             v = (Math.round((v-0)*100))/100;
12810             v = (v == Math.floor(v)) ? v + ".00" : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
12811             v = String(v);
12812             var ps = v.split('.'),
12813                 whole = ps[0],
12814                 sub = ps[1] ? '.'+ ps[1] : '.00',
12815                 r = /(\d+)(\d{3})/;
12816             while (r.test(whole)) {
12817                 whole = whole.replace(r, '$1' + ',' + '$2');
12818             }
12819             v = whole + sub;
12820             if(v.charAt(0) == '-'){
12821                 return '-$' + v.substr(1);
12822             }
12823             return "$" +  v;
12824         },
12825
12826         /**
12827          * Parse a value into a formatted date using the specified format pattern.
12828          * @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)
12829          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
12830          * @return {String} The formatted date string
12831          */
12832         date : function(v, format){
12833             if(!v){
12834                 return "";
12835             }
12836             if(!Ext.isDate(v)){
12837                 v = new Date(Date.parse(v));
12838             }
12839             return v.dateFormat(format || "m/d/Y");
12840         },
12841
12842         /**
12843          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
12844          * @param {String} format Any valid date format string
12845          * @return {Function} The date formatting function
12846          */
12847         dateRenderer : function(format){
12848             return function(v){
12849                 return Ext.util.Format.date(v, format);
12850             };
12851         },
12852
12853         /**
12854          * Strips all HTML tags
12855          * @param {Mixed} value The text from which to strip tags
12856          * @return {String} The stripped text
12857          */
12858         stripTags : function(v){
12859             return !v ? v : String(v).replace(stripTagsRE, "");
12860         },
12861
12862         /**
12863          * Strips all script tags
12864          * @param {Mixed} value The text from which to strip script tags
12865          * @return {String} The stripped text
12866          */
12867         stripScripts : function(v){
12868             return !v ? v : String(v).replace(stripScriptsRe, "");
12869         },
12870
12871         /**
12872          * Simple format for a file size (xxx bytes, xxx KB, xxx MB)
12873          * @param {Number/String} size The numeric value to format
12874          * @return {String} The formatted file size
12875          */
12876         fileSize : function(size){
12877             if(size < 1024) {
12878                 return size + " bytes";
12879             } else if(size < 1048576) {
12880                 return (Math.round(((size*10) / 1024))/10) + " KB";
12881             } else {
12882                 return (Math.round(((size*10) / 1048576))/10) + " MB";
12883             }
12884         },
12885
12886         /**
12887          * It does simple math for use in a template, for example:<pre><code>
12888          * var tpl = new Ext.Template('{value} * 10 = {value:math("* 10")}');
12889          * </code></pre>
12890          * @return {Function} A function that operates on the passed value.
12891          */
12892         math : function(){
12893             var fns = {};
12894             return function(v, a){
12895                 if(!fns[a]){
12896                     fns[a] = new Function('v', 'return v ' + a + ';');
12897                 }
12898                 return fns[a](v);
12899             }
12900         }(),
12901
12902         /**
12903          * Rounds the passed number to the required decimal precision.
12904          * @param {Number/String} value The numeric value to round.
12905          * @param {Number} precision The number of decimal places to which to round the first parameter's value.
12906          * @return {Number} The rounded value.
12907          */
12908         round : function(value, precision) {
12909             var result = Number(value);
12910             if (typeof precision == 'number') {
12911                 precision = Math.pow(10, precision);
12912                 result = Math.round(value * precision) / precision;
12913             }
12914             return result;
12915         },
12916
12917         /**
12918          * Formats the number according to the format string.
12919          * <div style="margin-left:40px">examples (123456.789):
12920          * <div style="margin-left:10px">
12921          * 0 - (123456) show only digits, no precision<br>
12922          * 0.00 - (123456.78) show only digits, 2 precision<br>
12923          * 0.0000 - (123456.7890) show only digits, 4 precision<br>
12924          * 0,000 - (123,456) show comma and digits, no precision<br>
12925          * 0,000.00 - (123,456.78) show comma and digits, 2 precision<br>
12926          * 0,0.00 - (123,456.78) shortcut method, show comma and digits, 2 precision<br>
12927          * To reverse the grouping (,) and decimal (.) for international numbers, add /i to the end.
12928          * For example: 0.000,00/i
12929          * </div></div>
12930          * @param {Number} v The number to format.
12931          * @param {String} format The way you would like to format this text.
12932          * @return {String} The formatted number.
12933          */
12934         number: function(v, format) {
12935             if(!format){
12936                 return v;
12937             }
12938             v = Ext.num(v, NaN);
12939             if (isNaN(v)){
12940                 return '';
12941             }
12942             var comma = ',',
12943                 dec = '.',
12944                 i18n = false,
12945                 neg = v < 0;
12946
12947             v = Math.abs(v);
12948             if(format.substr(format.length - 2) == '/i'){
12949                 format = format.substr(0, format.length - 2);
12950                 i18n = true;
12951                 comma = '.';
12952                 dec = ',';
12953             }
12954
12955             var hasComma = format.indexOf(comma) != -1,
12956                 psplit = (i18n ? format.replace(/[^\d\,]/g, '') : format.replace(/[^\d\.]/g, '')).split(dec);
12957
12958             if(1 < psplit.length){
12959                 v = v.toFixed(psplit[1].length);
12960             }else if(2 < psplit.length){
12961                 throw ('NumberFormatException: invalid format, formats should have no more than 1 period: ' + format);
12962             }else{
12963                 v = v.toFixed(0);
12964             }
12965
12966             var fnum = v.toString();
12967
12968             psplit = fnum.split('.');
12969
12970             if (hasComma) {
12971                 var cnum = psplit[0], parr = [], j = cnum.length, m = Math.floor(j / 3), n = cnum.length % 3 || 3;
12972
12973                 for (var i = 0; i < j; i += n) {
12974                     if (i != 0) {
12975                         n = 3;
12976                     }
12977                     parr[parr.length] = cnum.substr(i, n);
12978                     m -= 1;
12979                 }
12980                 fnum = parr.join(comma);
12981                 if (psplit[1]) {
12982                     fnum += dec + psplit[1];
12983                 }
12984             } else {
12985                 if (psplit[1]) {
12986                     fnum = psplit[0] + dec + psplit[1];
12987                 }
12988             }
12989
12990             return (neg ? '-' : '') + format.replace(/[\d,?\.?]+/, fnum);
12991         },
12992
12993         /**
12994          * Returns a number rendering function that can be reused to apply a number format multiple times efficiently
12995          * @param {String} format Any valid number format string for {@link #number}
12996          * @return {Function} The number formatting function
12997          */
12998         numberRenderer : function(format){
12999             return function(v){
13000                 return Ext.util.Format.number(v, format);
13001             };
13002         },
13003
13004         /**
13005          * Selectively do a plural form of a word based on a numeric value. For example, in a template,
13006          * {commentCount:plural("Comment")}  would result in "1 Comment" if commentCount was 1 or would be "x Comments"
13007          * if the value is 0 or greater than 1.
13008          * @param {Number} value The value to compare against
13009          * @param {String} singular The singular form of the word
13010          * @param {String} plural (optional) The plural form of the word (defaults to the singular with an "s")
13011          */
13012         plural : function(v, s, p){
13013             return v +' ' + (v == 1 ? s : (p ? p : s+'s'));
13014         },
13015
13016         /**
13017          * Converts newline characters to the HTML tag &lt;br/>
13018          * @param {String} The string value to format.
13019          * @return {String} The string with embedded &lt;br/> tags in place of newlines.
13020          */
13021         nl2br : function(v){
13022             return Ext.isEmpty(v) ? '' : v.replace(nl2brRe, '<br/>');
13023         }
13024     }
13025 }();
13026 /**
13027  * @class Ext.XTemplate
13028  * @extends Ext.Template
13029  * <p>A template class that supports advanced functionality like:<div class="mdetail-params"><ul>
13030  * <li>Autofilling arrays using templates and sub-templates</li>
13031  * <li>Conditional processing with basic comparison operators</li>
13032  * <li>Basic math function support</li>
13033  * <li>Execute arbitrary inline code with special built-in template variables</li>
13034  * <li>Custom member functions</li>
13035  * <li>Many special tags and built-in operators that aren't defined as part of
13036  * the API, but are supported in the templates that can be created</li>
13037  * </ul></div></p>
13038  * <p>XTemplate provides the templating mechanism built into:<div class="mdetail-params"><ul>
13039  * <li>{@link Ext.DataView}</li>
13040  * <li>{@link Ext.ListView}</li>
13041  * <li>{@link Ext.form.ComboBox}</li>
13042  * <li>{@link Ext.grid.TemplateColumn}</li>
13043  * <li>{@link Ext.grid.GroupingView}</li>
13044  * <li>{@link Ext.menu.Item}</li>
13045  * <li>{@link Ext.layout.MenuLayout}</li>
13046  * <li>{@link Ext.ColorPalette}</li>
13047  * </ul></div></p>
13048  *
13049  * <p>For example usage {@link #XTemplate see the constructor}.</p>
13050  *
13051  * @constructor
13052  * The {@link Ext.Template#Template Ext.Template constructor} describes
13053  * the acceptable parameters to pass to the constructor. The following
13054  * examples demonstrate all of the supported features.</p>
13055  *
13056  * <div class="mdetail-params"><ul>
13057  *
13058  * <li><b><u>Sample Data</u></b>
13059  * <div class="sub-desc">
13060  * <p>This is the data object used for reference in each code example:</p>
13061  * <pre><code>
13062 var data = {
13063     name: 'Jack Slocum',
13064     title: 'Lead Developer',
13065     company: 'Ext JS, LLC',
13066     email: 'jack@extjs.com',
13067     address: '4 Red Bulls Drive',
13068     city: 'Cleveland',
13069     state: 'Ohio',
13070     zip: '44102',
13071     drinks: ['Red Bull', 'Coffee', 'Water'],
13072     kids: [{
13073         name: 'Sara Grace',
13074         age:3
13075     },{
13076         name: 'Zachary',
13077         age:2
13078     },{
13079         name: 'John James',
13080         age:0
13081     }]
13082 };
13083  * </code></pre>
13084  * </div>
13085  * </li>
13086  *
13087  *
13088  * <li><b><u>Auto filling of arrays</u></b>
13089  * <div class="sub-desc">
13090  * <p>The <b><tt>tpl</tt></b> tag and the <b><tt>for</tt></b> operator are used
13091  * to process the provided data object:
13092  * <ul>
13093  * <li>If the value specified in <tt>for</tt> is an array, it will auto-fill,
13094  * repeating the template block inside the <tt>tpl</tt> tag for each item in the
13095  * array.</li>
13096  * <li>If <tt>for="."</tt> is specified, the data object provided is examined.</li>
13097  * <li>While processing an array, the special variable <tt>{#}</tt>
13098  * will provide the current array index + 1 (starts at 1, not 0).</li>
13099  * </ul>
13100  * </p>
13101  * <pre><code>
13102 &lt;tpl <b>for</b>=".">...&lt;/tpl>       // loop through array at root node
13103 &lt;tpl <b>for</b>="foo">...&lt;/tpl>     // loop through array at foo node
13104 &lt;tpl <b>for</b>="foo.bar">...&lt;/tpl> // loop through array at foo.bar node
13105  * </code></pre>
13106  * Using the sample data above:
13107  * <pre><code>
13108 var tpl = new Ext.XTemplate(
13109     '&lt;p>Kids: ',
13110     '&lt;tpl <b>for</b>=".">',       // process the data.kids node
13111         '&lt;p>{#}. {name}&lt;/p>',  // use current array index to autonumber
13112     '&lt;/tpl>&lt;/p>'
13113 );
13114 tpl.overwrite(panel.body, data.kids); // pass the kids property of the data object
13115  * </code></pre>
13116  * <p>An example illustrating how the <b><tt>for</tt></b> property can be leveraged
13117  * to access specified members of the provided data object to populate the template:</p>
13118  * <pre><code>
13119 var tpl = new Ext.XTemplate(
13120     '&lt;p>Name: {name}&lt;/p>',
13121     '&lt;p>Title: {title}&lt;/p>',
13122     '&lt;p>Company: {company}&lt;/p>',
13123     '&lt;p>Kids: ',
13124     '&lt;tpl <b>for="kids"</b>>',     // interrogate the kids property within the data
13125         '&lt;p>{name}&lt;/p>',
13126     '&lt;/tpl>&lt;/p>'
13127 );
13128 tpl.overwrite(panel.body, data);  // pass the root node of the data object
13129  * </code></pre>
13130  * <p>Flat arrays that contain values (and not objects) can be auto-rendered
13131  * using the special <b><tt>{.}</tt></b> variable inside a loop.  This variable
13132  * will represent the value of the array at the current index:</p>
13133  * <pre><code>
13134 var tpl = new Ext.XTemplate(
13135     '&lt;p>{name}\&#39;s favorite beverages:&lt;/p>',
13136     '&lt;tpl for="drinks">',
13137        '&lt;div> - {.}&lt;/div>',
13138     '&lt;/tpl>'
13139 );
13140 tpl.overwrite(panel.body, data);
13141  * </code></pre>
13142  * <p>When processing a sub-template, for example while looping through a child array,
13143  * you can access the parent object's members via the <b><tt>parent</tt></b> object:</p>
13144  * <pre><code>
13145 var tpl = new Ext.XTemplate(
13146     '&lt;p>Name: {name}&lt;/p>',
13147     '&lt;p>Kids: ',
13148     '&lt;tpl for="kids">',
13149         '&lt;tpl if="age > 1">',
13150             '&lt;p>{name}&lt;/p>',
13151             '&lt;p>Dad: {<b>parent</b>.name}&lt;/p>',
13152         '&lt;/tpl>',
13153     '&lt;/tpl>&lt;/p>'
13154 );
13155 tpl.overwrite(panel.body, data);
13156  * </code></pre>
13157  * </div>
13158  * </li>
13159  *
13160  *
13161  * <li><b><u>Conditional processing with basic comparison operators</u></b>
13162  * <div class="sub-desc">
13163  * <p>The <b><tt>tpl</tt></b> tag and the <b><tt>if</tt></b> operator are used
13164  * to provide conditional checks for deciding whether or not to render specific
13165  * parts of the template. Notes:<div class="sub-desc"><ul>
13166  * <li>Double quotes must be encoded if used within the conditional</li>
13167  * <li>There is no <tt>else</tt> operator &mdash; if needed, two opposite
13168  * <tt>if</tt> statements should be used.</li>
13169  * </ul></div>
13170  * <pre><code>
13171 &lt;tpl if="age &gt; 1 &amp;&amp; age &lt; 10">Child&lt;/tpl>
13172 &lt;tpl if="age >= 10 && age < 18">Teenager&lt;/tpl>
13173 &lt;tpl <b>if</b>="this.isGirl(name)">...&lt;/tpl>
13174 &lt;tpl <b>if</b>="id==\'download\'">...&lt;/tpl>
13175 &lt;tpl <b>if</b>="needsIcon">&lt;img src="{icon}" class="{iconCls}"/>&lt;/tpl>
13176 // no good:
13177 &lt;tpl if="name == "Jack"">Hello&lt;/tpl>
13178 // encode &#34; if it is part of the condition, e.g.
13179 &lt;tpl if="name == &#38;quot;Jack&#38;quot;">Hello&lt;/tpl>
13180  * </code></pre>
13181  * Using the sample data above:
13182  * <pre><code>
13183 var tpl = new Ext.XTemplate(
13184     '&lt;p>Name: {name}&lt;/p>',
13185     '&lt;p>Kids: ',
13186     '&lt;tpl for="kids">',
13187         '&lt;tpl if="age > 1">',
13188             '&lt;p>{name}&lt;/p>',
13189         '&lt;/tpl>',
13190     '&lt;/tpl>&lt;/p>'
13191 );
13192 tpl.overwrite(panel.body, data);
13193  * </code></pre>
13194  * </div>
13195  * </li>
13196  *
13197  *
13198  * <li><b><u>Basic math support</u></b>
13199  * <div class="sub-desc">
13200  * <p>The following basic math operators may be applied directly on numeric
13201  * data values:</p><pre>
13202  * + - * /
13203  * </pre>
13204  * For example:
13205  * <pre><code>
13206 var tpl = new Ext.XTemplate(
13207     '&lt;p>Name: {name}&lt;/p>',
13208     '&lt;p>Kids: ',
13209     '&lt;tpl for="kids">',
13210         '&lt;tpl if="age &amp;gt; 1">',  // <-- Note that the &gt; is encoded
13211             '&lt;p>{#}: {name}&lt;/p>',  // <-- Auto-number each item
13212             '&lt;p>In 5 Years: {age+5}&lt;/p>',  // <-- Basic math
13213             '&lt;p>Dad: {parent.name}&lt;/p>',
13214         '&lt;/tpl>',
13215     '&lt;/tpl>&lt;/p>'
13216 );
13217 tpl.overwrite(panel.body, data);
13218 </code></pre>
13219  * </div>
13220  * </li>
13221  *
13222  *
13223  * <li><b><u>Execute arbitrary inline code with special built-in template variables</u></b>
13224  * <div class="sub-desc">
13225  * <p>Anything between <code>{[ ... ]}</code> is considered code to be executed
13226  * in the scope of the template. There are some special variables available in that code:
13227  * <ul>
13228  * <li><b><tt>values</tt></b>: The values in the current scope. If you are using
13229  * scope changing sub-templates, you can change what <tt>values</tt> is.</li>
13230  * <li><b><tt>parent</tt></b>: The scope (values) of the ancestor template.</li>
13231  * <li><b><tt>xindex</tt></b>: If you are in a looping template, the index of the
13232  * loop you are in (1-based).</li>
13233  * <li><b><tt>xcount</tt></b>: If you are in a looping template, the total length
13234  * of the array you are looping.</li>
13235  * <li><b><tt>fm</tt></b>: An alias for <tt>Ext.util.Format</tt>.</li>
13236  * </ul>
13237  * This example demonstrates basic row striping using an inline code block and the
13238  * <tt>xindex</tt> variable:</p>
13239  * <pre><code>
13240 var tpl = new Ext.XTemplate(
13241     '&lt;p>Name: {name}&lt;/p>',
13242     '&lt;p>Company: {[values.company.toUpperCase() + ", " + values.title]}&lt;/p>',
13243     '&lt;p>Kids: ',
13244     '&lt;tpl for="kids">',
13245        '&lt;div class="{[xindex % 2 === 0 ? "even" : "odd"]}">',
13246         '{name}',
13247         '&lt;/div>',
13248     '&lt;/tpl>&lt;/p>'
13249 );
13250 tpl.overwrite(panel.body, data);
13251  * </code></pre>
13252  * </div>
13253  * </li>
13254  *
13255  * <li><b><u>Template member functions</u></b>
13256  * <div class="sub-desc">
13257  * <p>One or more member functions can be specified in a configuration
13258  * object passed into the XTemplate constructor for more complex processing:</p>
13259  * <pre><code>
13260 var tpl = new Ext.XTemplate(
13261     '&lt;p>Name: {name}&lt;/p>',
13262     '&lt;p>Kids: ',
13263     '&lt;tpl for="kids">',
13264         '&lt;tpl if="this.isGirl(name)">',
13265             '&lt;p>Girl: {name} - {age}&lt;/p>',
13266         '&lt;/tpl>',
13267         // use opposite if statement to simulate 'else' processing:
13268         '&lt;tpl if="this.isGirl(name) == false">',
13269             '&lt;p>Boy: {name} - {age}&lt;/p>',
13270         '&lt;/tpl>',
13271         '&lt;tpl if="this.isBaby(age)">',
13272             '&lt;p>{name} is a baby!&lt;/p>',
13273         '&lt;/tpl>',
13274     '&lt;/tpl>&lt;/p>',
13275     {
13276         // XTemplate configuration:
13277         compiled: true,
13278         disableFormats: true,
13279         // member functions:
13280         isGirl: function(name){
13281             return name == 'Sara Grace';
13282         },
13283         isBaby: function(age){
13284             return age < 1;
13285         }
13286     }
13287 );
13288 tpl.overwrite(panel.body, data);
13289  * </code></pre>
13290  * </div>
13291  * </li>
13292  *
13293  * </ul></div>
13294  *
13295  * @param {Mixed} config
13296  */
13297 Ext.XTemplate = function(){
13298     Ext.XTemplate.superclass.constructor.apply(this, arguments);
13299
13300     var me = this,
13301         s = me.html,
13302         re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
13303         nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
13304         ifRe = /^<tpl\b[^>]*?if="(.*?)"/,
13305         execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
13306         m,
13307         id = 0,
13308         tpls = [],
13309         VALUES = 'values',
13310         PARENT = 'parent',
13311         XINDEX = 'xindex',
13312         XCOUNT = 'xcount',
13313         RETURN = 'return ',
13314         WITHVALUES = 'with(values){ ';
13315
13316     s = ['<tpl>', s, '</tpl>'].join('');
13317
13318     while((m = s.match(re))){
13319         var m2 = m[0].match(nameRe),
13320             m3 = m[0].match(ifRe),
13321             m4 = m[0].match(execRe),
13322             exp = null,
13323             fn = null,
13324             exec = null,
13325             name = m2 && m2[1] ? m2[1] : '';
13326
13327        if (m3) {
13328            exp = m3 && m3[1] ? m3[1] : null;
13329            if(exp){
13330                fn = new Function(VALUES, PARENT, XINDEX, XCOUNT, WITHVALUES + RETURN +(Ext.util.Format.htmlDecode(exp))+'; }');
13331            }
13332        }
13333        if (m4) {
13334            exp = m4 && m4[1] ? m4[1] : null;
13335            if(exp){
13336                exec = new Function(VALUES, PARENT, XINDEX, XCOUNT, WITHVALUES +(Ext.util.Format.htmlDecode(exp))+'; }');
13337            }
13338        }
13339        if(name){
13340            switch(name){
13341                case '.': name = new Function(VALUES, PARENT, WITHVALUES + RETURN + VALUES + '; }'); break;
13342                case '..': name = new Function(VALUES, PARENT, WITHVALUES + RETURN + PARENT + '; }'); break;
13343                default: name = new Function(VALUES, PARENT, WITHVALUES + RETURN + name + '; }');
13344            }
13345        }
13346        tpls.push({
13347             id: id,
13348             target: name,
13349             exec: exec,
13350             test: fn,
13351             body: m[1]||''
13352         });
13353        s = s.replace(m[0], '{xtpl'+ id + '}');
13354        ++id;
13355     }
13356     for(var i = tpls.length-1; i >= 0; --i){
13357         me.compileTpl(tpls[i]);
13358     }
13359     me.master = tpls[tpls.length-1];
13360     me.tpls = tpls;
13361 };
13362 Ext.extend(Ext.XTemplate, Ext.Template, {
13363     // private
13364     re : /\{([\w-\.\#]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?(\s?[\+\-\*\\]\s?[\d\.\+\-\*\\\(\)]+)?\}/g,
13365     // private
13366     codeRe : /\{\[((?:\\\]|.|\n)*?)\]\}/g,
13367
13368     // private
13369     applySubTemplate : function(id, values, parent, xindex, xcount){
13370         var me = this,
13371             len,
13372             t = me.tpls[id],
13373             vs,
13374             buf = [];
13375         if ((t.test && !t.test.call(me, values, parent, xindex, xcount)) ||
13376             (t.exec && t.exec.call(me, values, parent, xindex, xcount))) {
13377             return '';
13378         }
13379         vs = t.target ? t.target.call(me, values, parent) : values;
13380         len = vs.length;
13381         parent = t.target ? values : parent;
13382         if(t.target && Ext.isArray(vs)){
13383             for(var i = 0, len = vs.length; i < len; i++){
13384                 buf[buf.length] = t.compiled.call(me, vs[i], parent, i+1, len);
13385             }
13386             return buf.join('');
13387         }
13388         return t.compiled.call(me, vs, parent, xindex, xcount);
13389     },
13390
13391     // private
13392     compileTpl : function(tpl){
13393         var fm = Ext.util.Format,
13394             useF = this.disableFormats !== true,
13395             sep = Ext.isGecko ? "+" : ",",
13396             body;
13397
13398         function fn(m, name, format, args, math){
13399             if(name.substr(0, 4) == 'xtpl'){
13400                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent, xindex, xcount)'+sep+"'";
13401             }
13402             var v;
13403             if(name === '.'){
13404                 v = 'values';
13405             }else if(name === '#'){
13406                 v = 'xindex';
13407             }else if(name.indexOf('.') != -1){
13408                 v = name;
13409             }else{
13410                 v = "values['" + name + "']";
13411             }
13412             if(math){
13413                 v = '(' + v + math + ')';
13414             }
13415             if (format && useF) {
13416                 args = args ? ',' + args : "";
13417                 if(format.substr(0, 5) != "this."){
13418                     format = "fm." + format + '(';
13419                 }else{
13420                     format = 'this.call("'+ format.substr(5) + '", ';
13421                     args = ", values";
13422                 }
13423             } else {
13424                 args= ''; format = "("+v+" === undefined ? '' : ";
13425             }
13426             return "'"+ sep + format + v + args + ")"+sep+"'";
13427         }
13428
13429         function codeFn(m, code){
13430             // Single quotes get escaped when the template is compiled, however we want to undo this when running code.
13431             return "'" + sep + '(' + code.replace(/\\'/g, "'") + ')' + sep + "'";
13432         }
13433
13434         // branched to use + in gecko and [].join() in others
13435         if(Ext.isGecko){
13436             body = "tpl.compiled = function(values, parent, xindex, xcount){ return '" +
13437                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn).replace(this.codeRe, codeFn) +
13438                     "';};";
13439         }else{
13440             body = ["tpl.compiled = function(values, parent, xindex, xcount){ return ['"];
13441             body.push(tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn).replace(this.codeRe, codeFn));
13442             body.push("'].join('');};");
13443             body = body.join('');
13444         }
13445         eval(body);
13446         return this;
13447     },
13448
13449     /**
13450      * Returns an HTML fragment of this template with the specified values applied.
13451      * @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'})
13452      * @return {String} The HTML fragment
13453      */
13454     applyTemplate : function(values){
13455         return this.master.compiled.call(this, values, {}, 1, 1);
13456     },
13457
13458     /**
13459      * Compile the template to a function for optimized performance.  Recommended if the template will be used frequently.
13460      * @return {Function} The compiled function
13461      */
13462     compile : function(){return this;}
13463
13464     /**
13465      * @property re
13466      * @hide
13467      */
13468     /**
13469      * @property disableFormats
13470      * @hide
13471      */
13472     /**
13473      * @method set
13474      * @hide
13475      */
13476
13477 });
13478 /**
13479  * Alias for {@link #applyTemplate}
13480  * Returns an HTML fragment of this template with the specified values applied.
13481  * @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'})
13482  * @return {String} The HTML fragment
13483  * @member Ext.XTemplate
13484  * @method apply
13485  */
13486 Ext.XTemplate.prototype.apply = Ext.XTemplate.prototype.applyTemplate;
13487
13488 /**
13489  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
13490  * @param {String/HTMLElement} el A DOM element or its id
13491  * @return {Ext.Template} The created template
13492  * @static
13493  */
13494 Ext.XTemplate.from = function(el){
13495     el = Ext.getDom(el);
13496     return new Ext.XTemplate(el.value || el.innerHTML);
13497 };
13498 /**
13499  * @class Ext.util.CSS
13500  * Utility class for manipulating CSS rules
13501  * @singleton
13502  */
13503 Ext.util.CSS = function(){
13504         var rules = null;
13505         var doc = document;
13506
13507     var camelRe = /(-[a-z])/gi;
13508     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13509
13510    return {
13511    /**
13512     * Creates a stylesheet from a text blob of rules.
13513     * These rules will be wrapped in a STYLE tag and appended to the HEAD of the document.
13514     * @param {String} cssText The text containing the css rules
13515     * @param {String} id An id to add to the stylesheet for later removal
13516     * @return {StyleSheet}
13517     */
13518    createStyleSheet : function(cssText, id){
13519        var ss;
13520        var head = doc.getElementsByTagName("head")[0];
13521        var rules = doc.createElement("style");
13522        rules.setAttribute("type", "text/css");
13523        if(id){
13524            rules.setAttribute("id", id);
13525        }
13526        if(Ext.isIE){
13527            head.appendChild(rules);
13528            ss = rules.styleSheet;
13529            ss.cssText = cssText;
13530        }else{
13531            try{
13532                 rules.appendChild(doc.createTextNode(cssText));
13533            }catch(e){
13534                rules.cssText = cssText;
13535            }
13536            head.appendChild(rules);
13537            ss = rules.styleSheet ? rules.styleSheet : (rules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13538        }
13539        this.cacheStyleSheet(ss);
13540        return ss;
13541    },
13542
13543    /**
13544     * Removes a style or link tag by id
13545     * @param {String} id The id of the tag
13546     */
13547    removeStyleSheet : function(id){
13548        var existing = doc.getElementById(id);
13549        if(existing){
13550            existing.parentNode.removeChild(existing);
13551        }
13552    },
13553
13554    /**
13555     * Dynamically swaps an existing stylesheet reference for a new one
13556     * @param {String} id The id of an existing link tag to remove
13557     * @param {String} url The href of the new stylesheet to include
13558     */
13559    swapStyleSheet : function(id, url){
13560        this.removeStyleSheet(id);
13561        var ss = doc.createElement("link");
13562        ss.setAttribute("rel", "stylesheet");
13563        ss.setAttribute("type", "text/css");
13564        ss.setAttribute("id", id);
13565        ss.setAttribute("href", url);
13566        doc.getElementsByTagName("head")[0].appendChild(ss);
13567    },
13568    
13569    /**
13570     * Refresh the rule cache if you have dynamically added stylesheets
13571     * @return {Object} An object (hash) of rules indexed by selector
13572     */
13573    refreshCache : function(){
13574        return this.getRules(true);
13575    },
13576
13577    // private
13578    cacheStyleSheet : function(ss){
13579        if(!rules){
13580            rules = {};
13581        }
13582        try{// try catch for cross domain access issue
13583            var ssRules = ss.cssRules || ss.rules;
13584            for(var j = ssRules.length-1; j >= 0; --j){
13585                rules[ssRules[j].selectorText.toLowerCase()] = ssRules[j];
13586            }
13587        }catch(e){}
13588    },
13589    
13590    /**
13591     * Gets all css rules for the document
13592     * @param {Boolean} refreshCache true to refresh the internal cache
13593     * @return {Object} An object (hash) of rules indexed by selector
13594     */
13595    getRules : function(refreshCache){
13596                 if(rules === null || refreshCache){
13597                         rules = {};
13598                         var ds = doc.styleSheets;
13599                         for(var i =0, len = ds.length; i < len; i++){
13600                             try{
13601                         this.cacheStyleSheet(ds[i]);
13602                     }catch(e){} 
13603                 }
13604                 }
13605                 return rules;
13606         },
13607         
13608         /**
13609     * Gets an an individual CSS rule by selector(s)
13610     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
13611     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
13612     * @return {CSSRule} The CSS rule or null if one is not found
13613     */
13614    getRule : function(selector, refreshCache){
13615                 var rs = this.getRules(refreshCache);
13616                 if(!Ext.isArray(selector)){
13617                     return rs[selector.toLowerCase()];
13618                 }
13619                 for(var i = 0; i < selector.length; i++){
13620                         if(rs[selector[i]]){
13621                                 return rs[selector[i].toLowerCase()];
13622                         }
13623                 }
13624                 return null;
13625         },
13626         
13627         
13628         /**
13629     * Updates a rule property
13630     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
13631     * @param {String} property The css property
13632     * @param {String} value The new value for the property
13633     * @return {Boolean} true If a rule was found and updated
13634     */
13635    updateRule : function(selector, property, value){
13636                 if(!Ext.isArray(selector)){
13637                         var rule = this.getRule(selector);
13638                         if(rule){
13639                                 rule.style[property.replace(camelRe, camelFn)] = value;
13640                                 return true;
13641                         }
13642                 }else{
13643                         for(var i = 0; i < selector.length; i++){
13644                                 if(this.updateRule(selector[i], property, value)){
13645                                         return true;
13646                                 }
13647                         }
13648                 }
13649                 return false;
13650         }
13651    };   
13652 }();/**
13653  @class Ext.util.ClickRepeater
13654  @extends Ext.util.Observable
13655
13656  A wrapper class which can be applied to any element. Fires a "click" event while the
13657  mouse is pressed. The interval between firings may be specified in the config but
13658  defaults to 20 milliseconds.
13659
13660  Optionally, a CSS class may be applied to the element during the time it is pressed.
13661
13662  @cfg {Mixed} el The element to act as a button.
13663  @cfg {Number} delay The initial delay before the repeating event begins firing.
13664  Similar to an autorepeat key delay.
13665  @cfg {Number} interval The interval between firings of the "click" event. Default 20 ms.
13666  @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
13667  @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
13668            "interval" and "delay" are ignored.
13669  @cfg {Boolean} preventDefault True to prevent the default click event
13670  @cfg {Boolean} stopDefault True to stop the default click event
13671
13672  @history
13673     2007-02-02 jvs Original code contributed by Nige "Animal" White
13674     2007-02-02 jvs Renamed to ClickRepeater
13675     2007-02-03 jvs Modifications for FF Mac and Safari
13676
13677  @constructor
13678  @param {Mixed} el The element to listen on
13679  @param {Object} config
13680  */
13681 Ext.util.ClickRepeater = Ext.extend(Ext.util.Observable, {
13682     
13683     constructor : function(el, config){
13684         this.el = Ext.get(el);
13685         this.el.unselectable();
13686
13687         Ext.apply(this, config);
13688
13689         this.addEvents(
13690         /**
13691          * @event mousedown
13692          * Fires when the mouse button is depressed.
13693          * @param {Ext.util.ClickRepeater} this
13694          * @param {Ext.EventObject} e
13695          */
13696         "mousedown",
13697         /**
13698          * @event click
13699          * Fires on a specified interval during the time the element is pressed.
13700          * @param {Ext.util.ClickRepeater} this
13701          * @param {Ext.EventObject} e
13702          */
13703         "click",
13704         /**
13705          * @event mouseup
13706          * Fires when the mouse key is released.
13707          * @param {Ext.util.ClickRepeater} this
13708          * @param {Ext.EventObject} e
13709          */
13710         "mouseup"
13711         );
13712
13713         if(!this.disabled){
13714             this.disabled = true;
13715             this.enable();
13716         }
13717
13718         // allow inline handler
13719         if(this.handler){
13720             this.on("click", this.handler,  this.scope || this);
13721         }
13722
13723         Ext.util.ClickRepeater.superclass.constructor.call(this);        
13724     },
13725     
13726     interval : 20,
13727     delay: 250,
13728     preventDefault : true,
13729     stopDefault : false,
13730     timer : 0,
13731
13732     /**
13733      * Enables the repeater and allows events to fire.
13734      */
13735     enable: function(){
13736         if(this.disabled){
13737             this.el.on('mousedown', this.handleMouseDown, this);
13738             if (Ext.isIE){
13739                 this.el.on('dblclick', this.handleDblClick, this);
13740             }
13741             if(this.preventDefault || this.stopDefault){
13742                 this.el.on('click', this.eventOptions, this);
13743             }
13744         }
13745         this.disabled = false;
13746     },
13747
13748     /**
13749      * Disables the repeater and stops events from firing.
13750      */
13751     disable: function(/* private */ force){
13752         if(force || !this.disabled){
13753             clearTimeout(this.timer);
13754             if(this.pressClass){
13755                 this.el.removeClass(this.pressClass);
13756             }
13757             Ext.getDoc().un('mouseup', this.handleMouseUp, this);
13758             this.el.removeAllListeners();
13759         }
13760         this.disabled = true;
13761     },
13762
13763     /**
13764      * Convenience function for setting disabled/enabled by boolean.
13765      * @param {Boolean} disabled
13766      */
13767     setDisabled: function(disabled){
13768         this[disabled ? 'disable' : 'enable']();
13769     },
13770
13771     eventOptions: function(e){
13772         if(this.preventDefault){
13773             e.preventDefault();
13774         }
13775         if(this.stopDefault){
13776             e.stopEvent();
13777         }
13778     },
13779
13780     // private
13781     destroy : function() {
13782         this.disable(true);
13783         Ext.destroy(this.el);
13784         this.purgeListeners();
13785     },
13786
13787     handleDblClick : function(e){
13788         clearTimeout(this.timer);
13789         this.el.blur();
13790
13791         this.fireEvent("mousedown", this, e);
13792         this.fireEvent("click", this, e);
13793     },
13794
13795     // private
13796     handleMouseDown : function(e){
13797         clearTimeout(this.timer);
13798         this.el.blur();
13799         if(this.pressClass){
13800             this.el.addClass(this.pressClass);
13801         }
13802         this.mousedownTime = new Date();
13803
13804         Ext.getDoc().on("mouseup", this.handleMouseUp, this);
13805         this.el.on("mouseout", this.handleMouseOut, this);
13806
13807         this.fireEvent("mousedown", this, e);
13808         this.fireEvent("click", this, e);
13809
13810         // Do not honor delay or interval if acceleration wanted.
13811         if (this.accelerate) {
13812             this.delay = 400;
13813         }
13814         this.timer = this.click.defer(this.delay || this.interval, this, [e]);
13815     },
13816
13817     // private
13818     click : function(e){
13819         this.fireEvent("click", this, e);
13820         this.timer = this.click.defer(this.accelerate ?
13821             this.easeOutExpo(this.mousedownTime.getElapsed(),
13822                 400,
13823                 -390,
13824                 12000) :
13825             this.interval, this, [e]);
13826     },
13827
13828     easeOutExpo : function (t, b, c, d) {
13829         return (t==d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b;
13830     },
13831
13832     // private
13833     handleMouseOut : function(){
13834         clearTimeout(this.timer);
13835         if(this.pressClass){
13836             this.el.removeClass(this.pressClass);
13837         }
13838         this.el.on("mouseover", this.handleMouseReturn, this);
13839     },
13840
13841     // private
13842     handleMouseReturn : function(){
13843         this.el.un("mouseover", this.handleMouseReturn, this);
13844         if(this.pressClass){
13845             this.el.addClass(this.pressClass);
13846         }
13847         this.click();
13848     },
13849
13850     // private
13851     handleMouseUp : function(e){
13852         clearTimeout(this.timer);
13853         this.el.un("mouseover", this.handleMouseReturn, this);
13854         this.el.un("mouseout", this.handleMouseOut, this);
13855         Ext.getDoc().un("mouseup", this.handleMouseUp, this);
13856         this.el.removeClass(this.pressClass);
13857         this.fireEvent("mouseup", this, e);
13858     }
13859 });/**
13860  * @class Ext.KeyNav
13861  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
13862  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
13863  * way to implement custom navigation schemes for any UI component.</p>
13864  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
13865  * pageUp, pageDown, del, home, end.  Usage:</p>
13866  <pre><code>
13867 var nav = new Ext.KeyNav("my-element", {
13868     "left" : function(e){
13869         this.moveLeft(e.ctrlKey);
13870     },
13871     "right" : function(e){
13872         this.moveRight(e.ctrlKey);
13873     },
13874     "enter" : function(e){
13875         this.save();
13876     },
13877     scope : this
13878 });
13879 </code></pre>
13880  * @constructor
13881  * @param {Mixed} el The element to bind to
13882  * @param {Object} config The config
13883  */
13884 Ext.KeyNav = function(el, config){
13885     this.el = Ext.get(el);
13886     Ext.apply(this, config);
13887     if(!this.disabled){
13888         this.disabled = true;
13889         this.enable();
13890     }
13891 };
13892
13893 Ext.KeyNav.prototype = {
13894     /**
13895      * @cfg {Boolean} disabled
13896      * True to disable this KeyNav instance (defaults to false)
13897      */
13898     disabled : false,
13899     /**
13900      * @cfg {String} defaultEventAction
13901      * The method to call on the {@link Ext.EventObject} after this KeyNav intercepts a key.  Valid values are
13902      * {@link Ext.EventObject#stopEvent}, {@link Ext.EventObject#preventDefault} and
13903      * {@link Ext.EventObject#stopPropagation} (defaults to 'stopEvent')
13904      */
13905     defaultEventAction: "stopEvent",
13906     /**
13907      * @cfg {Boolean} forceKeyDown
13908      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
13909      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
13910      * handle keydown instead of keypress.
13911      */
13912     forceKeyDown : false,
13913
13914     // private
13915     relay : function(e){
13916         var k = e.getKey();
13917         var h = this.keyToHandler[k];
13918         if(h && this[h]){
13919             if(this.doRelay(e, this[h], h) !== true){
13920                 e[this.defaultEventAction]();
13921             }
13922         }
13923     },
13924
13925     // private
13926     doRelay : function(e, h, hname){
13927         return h.call(this.scope || this, e);
13928     },
13929
13930     // possible handlers
13931     enter : false,
13932     left : false,
13933     right : false,
13934     up : false,
13935     down : false,
13936     tab : false,
13937     esc : false,
13938     pageUp : false,
13939     pageDown : false,
13940     del : false,
13941     home : false,
13942     end : false,
13943
13944     // quick lookup hash
13945     keyToHandler : {
13946         37 : "left",
13947         39 : "right",
13948         38 : "up",
13949         40 : "down",
13950         33 : "pageUp",
13951         34 : "pageDown",
13952         46 : "del",
13953         36 : "home",
13954         35 : "end",
13955         13 : "enter",
13956         27 : "esc",
13957         9  : "tab"
13958     },
13959     
13960     stopKeyUp: function(e) {
13961         var k = e.getKey();
13962
13963         if (k >= 37 && k <= 40) {
13964             // *** bugfix - safari 2.x fires 2 keyup events on cursor keys
13965             // *** (note: this bugfix sacrifices the "keyup" event originating from keyNav elements in Safari 2)
13966             e.stopEvent();
13967         }
13968     },
13969     
13970     /**
13971      * Destroy this KeyNav (this is the same as calling disable).
13972      */
13973     destroy: function(){
13974         this.disable();    
13975     },
13976
13977         /**
13978          * Enable this KeyNav
13979          */
13980         enable: function() {
13981         if (this.disabled) {
13982             if (Ext.isSafari2) {
13983                 // call stopKeyUp() on "keyup" event
13984                 this.el.on('keyup', this.stopKeyUp, this);
13985             }
13986
13987             this.el.on(this.isKeydown()? 'keydown' : 'keypress', this.relay, this);
13988             this.disabled = false;
13989         }
13990     },
13991
13992         /**
13993          * Disable this KeyNav
13994          */
13995         disable: function() {
13996         if (!this.disabled) {
13997             if (Ext.isSafari2) {
13998                 // remove "keyup" event handler
13999                 this.el.un('keyup', this.stopKeyUp, this);
14000             }
14001
14002             this.el.un(this.isKeydown()? 'keydown' : 'keypress', this.relay, this);
14003             this.disabled = true;
14004         }
14005     },
14006     
14007     /**
14008      * Convenience function for setting disabled/enabled by boolean.
14009      * @param {Boolean} disabled
14010      */
14011     setDisabled : function(disabled){
14012         this[disabled ? "disable" : "enable"]();
14013     },
14014     
14015     // private
14016     isKeydown: function(){
14017         return this.forceKeyDown || Ext.EventManager.useKeydown;
14018     }
14019 };
14020 /**
14021  * @class Ext.KeyMap
14022  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
14023  * The constructor accepts the same config object as defined by {@link #addBinding}.
14024  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
14025  * combination it will call the function with this signature (if the match is a multi-key
14026  * combination the callback will still be called only once): (String key, Ext.EventObject e)
14027  * A KeyMap can also handle a string representation of keys.<br />
14028  * Usage:
14029  <pre><code>
14030 // map one key by key code
14031 var map = new Ext.KeyMap("my-element", {
14032     key: 13, // or Ext.EventObject.ENTER
14033     fn: myHandler,
14034     scope: myObject
14035 });
14036
14037 // map multiple keys to one action by string
14038 var map = new Ext.KeyMap("my-element", {
14039     key: "a\r\n\t",
14040     fn: myHandler,
14041     scope: myObject
14042 });
14043
14044 // map multiple keys to multiple actions by strings and array of codes
14045 var map = new Ext.KeyMap("my-element", [
14046     {
14047         key: [10,13],
14048         fn: function(){ alert("Return was pressed"); }
14049     }, {
14050         key: "abc",
14051         fn: function(){ alert('a, b or c was pressed'); }
14052     }, {
14053         key: "\t",
14054         ctrl:true,
14055         shift:true,
14056         fn: function(){ alert('Control + shift + tab was pressed.'); }
14057     }
14058 ]);
14059 </code></pre>
14060  * <b>Note: A KeyMap starts enabled</b>
14061  * @constructor
14062  * @param {Mixed} el The element to bind to
14063  * @param {Object} config The config (see {@link #addBinding})
14064  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
14065  */
14066 Ext.KeyMap = function(el, config, eventName){
14067     this.el  = Ext.get(el);
14068     this.eventName = eventName || "keydown";
14069     this.bindings = [];
14070     if(config){
14071         this.addBinding(config);
14072     }
14073     this.enable();
14074 };
14075
14076 Ext.KeyMap.prototype = {
14077     /**
14078      * True to stop the event from bubbling and prevent the default browser action if the
14079      * key was handled by the KeyMap (defaults to false)
14080      * @type Boolean
14081      */
14082     stopEvent : false,
14083
14084     /**
14085      * Add a new binding to this KeyMap. The following config object properties are supported:
14086      * <pre>
14087 Property    Type             Description
14088 ----------  ---------------  ----------------------------------------------------------------------
14089 key         String/Array     A single keycode or an array of keycodes to handle
14090 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)
14091 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)
14092 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)
14093 handler     Function         The function to call when KeyMap finds the expected key combination
14094 fn          Function         Alias of handler (for backwards-compatibility)
14095 scope       Object           The scope of the callback function
14096 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)
14097 </pre>
14098      *
14099      * Usage:
14100      * <pre><code>
14101 // Create a KeyMap
14102 var map = new Ext.KeyMap(document, {
14103     key: Ext.EventObject.ENTER,
14104     fn: handleKey,
14105     scope: this
14106 });
14107
14108 //Add a new binding to the existing KeyMap later
14109 map.addBinding({
14110     key: 'abc',
14111     shift: true,
14112     fn: handleKey,
14113     scope: this
14114 });
14115 </code></pre>
14116      * @param {Object/Array} config A single KeyMap config or an array of configs
14117      */
14118         addBinding : function(config){
14119         if(Ext.isArray(config)){
14120             Ext.each(config, function(c){
14121                 this.addBinding(c);
14122             }, this);
14123             return;
14124         }
14125         var keyCode = config.key,
14126             fn = config.fn || config.handler,
14127             scope = config.scope;
14128
14129         if (config.stopEvent) {
14130             this.stopEvent = config.stopEvent;    
14131         }       
14132
14133         if(typeof keyCode == "string"){
14134             var ks = [];
14135             var keyString = keyCode.toUpperCase();
14136             for(var j = 0, len = keyString.length; j < len; j++){
14137                 ks.push(keyString.charCodeAt(j));
14138             }
14139             keyCode = ks;
14140         }
14141         var keyArray = Ext.isArray(keyCode);
14142         
14143         var handler = function(e){
14144             if(this.checkModifiers(config, e)){
14145                 var k = e.getKey();
14146                 if(keyArray){
14147                     for(var i = 0, len = keyCode.length; i < len; i++){
14148                         if(keyCode[i] == k){
14149                           if(this.stopEvent){
14150                               e.stopEvent();
14151                           }
14152                           fn.call(scope || window, k, e);
14153                           return;
14154                         }
14155                     }
14156                 }else{
14157                     if(k == keyCode){
14158                         if(this.stopEvent){
14159                            e.stopEvent();
14160                         }
14161                         fn.call(scope || window, k, e);
14162                     }
14163                 }
14164             }
14165         };
14166         this.bindings.push(handler);
14167         },
14168     
14169     // private
14170     checkModifiers: function(config, e){
14171         var val, key, keys = ['shift', 'ctrl', 'alt'];
14172         for (var i = 0, len = keys.length; i < len; ++i){
14173             key = keys[i];
14174             val = config[key];
14175             if(!(val === undefined || (val === e[key + 'Key']))){
14176                 return false;
14177             }
14178         }
14179         return true;
14180     },
14181
14182     /**
14183      * Shorthand for adding a single key listener
14184      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14185      * following options:
14186      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14187      * @param {Function} fn The function to call
14188      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the browser window.
14189      */
14190     on : function(key, fn, scope){
14191         var keyCode, shift, ctrl, alt;
14192         if(typeof key == "object" && !Ext.isArray(key)){
14193             keyCode = key.key;
14194             shift = key.shift;
14195             ctrl = key.ctrl;
14196             alt = key.alt;
14197         }else{
14198             keyCode = key;
14199         }
14200         this.addBinding({
14201             key: keyCode,
14202             shift: shift,
14203             ctrl: ctrl,
14204             alt: alt,
14205             fn: fn,
14206             scope: scope
14207         });
14208     },
14209
14210     // private
14211     handleKeyDown : function(e){
14212             if(this.enabled){ //just in case
14213             var b = this.bindings;
14214             for(var i = 0, len = b.length; i < len; i++){
14215                 b[i].call(this, e);
14216             }
14217             }
14218         },
14219
14220         /**
14221          * Returns true if this KeyMap is enabled
14222          * @return {Boolean}
14223          */
14224         isEnabled : function(){
14225             return this.enabled;
14226         },
14227
14228         /**
14229          * Enables this KeyMap
14230          */
14231         enable: function(){
14232                 if(!this.enabled){
14233                     this.el.on(this.eventName, this.handleKeyDown, this);
14234                     this.enabled = true;
14235                 }
14236         },
14237
14238         /**
14239          * Disable this KeyMap
14240          */
14241         disable: function(){
14242                 if(this.enabled){
14243                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14244                     this.enabled = false;
14245                 }
14246         },
14247     
14248     /**
14249      * Convenience function for setting disabled/enabled by boolean.
14250      * @param {Boolean} disabled
14251      */
14252     setDisabled : function(disabled){
14253         this[disabled ? "disable" : "enable"]();
14254     }
14255 };/**
14256  * @class Ext.util.TextMetrics
14257  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14258  * wide, in pixels, a given block of text will be. Note that when measuring text, it should be plain text and
14259  * should not contain any HTML, otherwise it may not be measured correctly.
14260  * @singleton
14261  */
14262 Ext.util.TextMetrics = function(){
14263     var shared;
14264     return {
14265         /**
14266          * Measures the size of the specified text
14267          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14268          * that can affect the size of the rendered text
14269          * @param {String} text The text to measure
14270          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14271          * in order to accurately measure the text height
14272          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14273          */
14274         measure : function(el, text, fixedWidth){
14275             if(!shared){
14276                 shared = Ext.util.TextMetrics.Instance(el, fixedWidth);
14277             }
14278             shared.bind(el);
14279             shared.setFixedWidth(fixedWidth || 'auto');
14280             return shared.getSize(text);
14281         },
14282
14283         /**
14284          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14285          * the overhead of multiple calls to initialize the style properties on each measurement.
14286          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14287          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14288          * in order to accurately measure the text height
14289          * @return {Ext.util.TextMetrics.Instance} instance The new instance
14290          */
14291         createInstance : function(el, fixedWidth){
14292             return Ext.util.TextMetrics.Instance(el, fixedWidth);
14293         }
14294     };
14295 }();
14296
14297 Ext.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14298     var ml = new Ext.Element(document.createElement('div'));
14299     document.body.appendChild(ml.dom);
14300     ml.position('absolute');
14301     ml.setLeftTop(-1000, -1000);
14302     ml.hide();
14303
14304     if(fixedWidth){
14305         ml.setWidth(fixedWidth);
14306     }
14307
14308     var instance = {
14309         /**
14310          * <p><b>Only available on the instance returned from {@link #createInstance}, <u>not</u> on the singleton.</b></p>
14311          * Returns the size of the specified text based on the internal element's style and width properties
14312          * @param {String} text The text to measure
14313          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14314          */
14315         getSize : function(text){
14316             ml.update(text);
14317             var s = ml.getSize();
14318             ml.update('');
14319             return s;
14320         },
14321
14322         /**
14323          * <p><b>Only available on the instance returned from {@link #createInstance}, <u>not</u> on the singleton.</b></p>
14324          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14325          * that can affect the size of the rendered text
14326          * @param {String/HTMLElement} el The element, dom node or id
14327          */
14328         bind : function(el){
14329             ml.setStyle(
14330                 Ext.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height', 'text-transform', 'letter-spacing')
14331             );
14332         },
14333
14334         /**
14335          * <p><b>Only available on the instance returned from {@link #createInstance}, <u>not</u> on the singleton.</b></p>
14336          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14337          * to set a fixed width in order to accurately measure the text height.
14338          * @param {Number} width The width to set on the element
14339          */
14340         setFixedWidth : function(width){
14341             ml.setWidth(width);
14342         },
14343
14344         /**
14345          * <p><b>Only available on the instance returned from {@link #createInstance}, <u>not</u> on the singleton.</b></p>
14346          * Returns the measured width of the specified text
14347          * @param {String} text The text to measure
14348          * @return {Number} width The width in pixels
14349          */
14350         getWidth : function(text){
14351             ml.dom.style.width = 'auto';
14352             return this.getSize(text).width;
14353         },
14354
14355         /**
14356          * <p><b>Only available on the instance returned from {@link #createInstance}, <u>not</u> on the singleton.</b></p>
14357          * Returns the measured height of the specified text.  For multiline text, be sure to call
14358          * {@link #setFixedWidth} if necessary.
14359          * @param {String} text The text to measure
14360          * @return {Number} height The height in pixels
14361          */
14362         getHeight : function(text){
14363             return this.getSize(text).height;
14364         }
14365     };
14366
14367     instance.bind(bindTo);
14368
14369     return instance;
14370 };
14371
14372 Ext.Element.addMethods({
14373     /**
14374      * Returns the width in pixels of the passed text, or the width of the text in this Element.
14375      * @param {String} text The text to measure. Defaults to the innerHTML of the element.
14376      * @param {Number} min (Optional) The minumum value to return.
14377      * @param {Number} max (Optional) The maximum value to return.
14378      * @return {Number} The text width in pixels.
14379      * @member Ext.Element getTextWidth
14380      */
14381     getTextWidth : function(text, min, max){
14382         return (Ext.util.TextMetrics.measure(this.dom, Ext.value(text, this.dom.innerHTML, true)).width).constrain(min || 0, max || 1000000);
14383     }
14384 });
14385 /**
14386  * @class Ext.util.Cookies
14387  * Utility class for managing and interacting with cookies.
14388  * @singleton
14389  */
14390 Ext.util.Cookies = {
14391     /**
14392      * Create a cookie with the specified name and value. Additional settings
14393      * for the cookie may be optionally specified (for example: expiration,
14394      * access restriction, SSL).
14395      * @param {String} name The name of the cookie to set. 
14396      * @param {Mixed} value The value to set for the cookie.
14397      * @param {Object} expires (Optional) Specify an expiration date the
14398      * cookie is to persist until.  Note that the specified Date object will
14399      * be converted to Greenwich Mean Time (GMT). 
14400      * @param {String} path (Optional) Setting a path on the cookie restricts
14401      * access to pages that match that path. Defaults to all pages (<tt>'/'</tt>). 
14402      * @param {String} domain (Optional) Setting a domain restricts access to
14403      * pages on a given domain (typically used to allow cookie access across
14404      * subdomains). For example, "extjs.com" will create a cookie that can be
14405      * accessed from any subdomain of extjs.com, including www.extjs.com,
14406      * support.extjs.com, etc.
14407      * @param {Boolean} secure (Optional) Specify true to indicate that the cookie
14408      * should only be accessible via SSL on a page using the HTTPS protocol.
14409      * Defaults to <tt>false</tt>. Note that this will only work if the page
14410      * calling this code uses the HTTPS protocol, otherwise the cookie will be
14411      * created with default options.
14412      */
14413     set : function(name, value){
14414         var argv = arguments;
14415         var argc = arguments.length;
14416         var expires = (argc > 2) ? argv[2] : null;
14417         var path = (argc > 3) ? argv[3] : '/';
14418         var domain = (argc > 4) ? argv[4] : null;
14419         var secure = (argc > 5) ? argv[5] : false;
14420         document.cookie = name + "=" + escape(value) + ((expires === null) ? "" : ("; expires=" + expires.toGMTString())) + ((path === null) ? "" : ("; path=" + path)) + ((domain === null) ? "" : ("; domain=" + domain)) + ((secure === true) ? "; secure" : "");
14421     },
14422
14423     /**
14424      * Retrieves cookies that are accessible by the current page. If a cookie
14425      * does not exist, <code>get()</code> returns <tt>null</tt>.  The following
14426      * example retrieves the cookie called "valid" and stores the String value
14427      * in the variable <tt>validStatus</tt>.
14428      * <pre><code>
14429      * var validStatus = Ext.util.Cookies.get("valid");
14430      * </code></pre>
14431      * @param {String} name The name of the cookie to get
14432      * @return {Mixed} Returns the cookie value for the specified name;
14433      * null if the cookie name does not exist.
14434      */
14435     get : function(name){
14436         var arg = name + "=";
14437         var alen = arg.length;
14438         var clen = document.cookie.length;
14439         var i = 0;
14440         var j = 0;
14441         while(i < clen){
14442             j = i + alen;
14443             if(document.cookie.substring(i, j) == arg){
14444                 return Ext.util.Cookies.getCookieVal(j);
14445             }
14446             i = document.cookie.indexOf(" ", i) + 1;
14447             if(i === 0){
14448                 break;
14449             }
14450         }
14451         return null;
14452     },
14453
14454     /**
14455      * Removes a cookie with the provided name from the browser
14456      * if found by setting its expiration date to sometime in the past. 
14457      * @param {String} name The name of the cookie to remove
14458      */
14459     clear : function(name){
14460         if(Ext.util.Cookies.get(name)){
14461             document.cookie = name + "=" + "; expires=Thu, 01-Jan-70 00:00:01 GMT";
14462         }
14463     },
14464     /**
14465      * @private
14466      */
14467     getCookieVal : function(offset){
14468         var endstr = document.cookie.indexOf(";", offset);
14469         if(endstr == -1){
14470             endstr = document.cookie.length;
14471         }
14472         return unescape(document.cookie.substring(offset, endstr));
14473     }
14474 };/**
14475  * Framework-wide error-handler.  Developers can override this method to provide
14476  * custom exception-handling.  Framework errors will often extend from the base
14477  * Ext.Error class.
14478  * @param {Object/Error} e The thrown exception object.
14479  */
14480 Ext.handleError = function(e) {
14481     throw e;
14482 };
14483
14484 /**
14485  * @class Ext.Error
14486  * @extends Error
14487  * <p>A base error class. Future implementations are intended to provide more
14488  * robust error handling throughout the framework (<b>in the debug build only</b>)
14489  * to check for common errors and problems. The messages issued by this class
14490  * will aid error checking. Error checks will be automatically removed in the
14491  * production build so that performance is not negatively impacted.</p>
14492  * <p>Some sample messages currently implemented:</p><pre>
14493 "DataProxy attempted to execute an API-action but found an undefined
14494 url / function. Please review your Proxy url/api-configuration."
14495  * </pre><pre>
14496 "Could not locate your "root" property in your server response.
14497 Please review your JsonReader config to ensure the config-property
14498 "root" matches the property your server-response.  See the JsonReader
14499 docs for additional assistance."
14500  * </pre>
14501  * <p>An example of the code used for generating error messages:</p><pre><code>
14502 try {
14503     generateError({
14504         foo: 'bar'
14505     });
14506 }
14507 catch (e) {
14508     console.error(e);
14509 }
14510 function generateError(data) {
14511     throw new Ext.Error('foo-error', data);
14512 }
14513  * </code></pre>
14514  * @param {String} message
14515  */
14516 Ext.Error = function(message) {
14517     // Try to read the message from Ext.Error.lang
14518     this.message = (this.lang[message]) ? this.lang[message] : message;
14519 };
14520
14521 Ext.Error.prototype = new Error();
14522 Ext.apply(Ext.Error.prototype, {
14523     // protected.  Extensions place their error-strings here.
14524     lang: {},
14525
14526     name: 'Ext.Error',
14527     /**
14528      * getName
14529      * @return {String}
14530      */
14531     getName : function() {
14532         return this.name;
14533     },
14534     /**
14535      * getMessage
14536      * @return {String}
14537      */
14538     getMessage : function() {
14539         return this.message;
14540     },
14541     /**
14542      * toJson
14543      * @return {String}
14544      */
14545     toJson : function() {
14546         return Ext.encode(this);
14547     }
14548 });